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 extends Check> 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 extends Check>> 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 buttons;
+ private String title;
+ private int size;
+
+ private Inventory inv;
+
+ private boolean partiallyTouchable;
+ private int[] allowedSlots;
+
+ public Gui(String title, int size){
+
+ if(!(guis.contains(this))) guis.add(this);
+
+ this.pages = new HashMap<>();
+ this.items = new HashMap<>();
+ this.buttons = new HashSet<>();
+ this.title = title;
+ this.size = size;
+ this.partiallyTouchable = false;
+ this.allowedSlots = new int[0];
+
+ this.inv = null;
+
+ init();
+ }
+
+ public boolean isPartiallyTouchable() { return this.partiallyTouchable; }
+
+ public void setPartiallyTouchable(boolean value) { this.partiallyTouchable = value; }
+
+ public int[] getAllowedSlots() { return this.allowedSlots; }
+
+ public void setAllowedSlots(int... slots) { this.allowedSlots = slots; }
+
+ public boolean clickedAllowedSlot(int clickedSlot) {
+ if(this.allowedSlots.length < 1) return false;
+ for(int i = 0; i < this.allowedSlots.length; i++) {
+ int allowedSlot = this.allowedSlots[i];
+ if(clickedSlot == allowedSlot) return true;
+ }
+ return false;
+ }
+
+ public HashMap getPages() { return pages; }
+
+ private String getTitle() { return title; }
+
+ public int getSize() { return size; }
+
+ private void init(){ createPage(false); }
+
+ public ItemStack getItem(int pos) {
+ HashMap items = this.items.get(1);
+ if(items != null) if(!items.isEmpty() && items.containsKey(pos)) return items.get(pos);
+ return null;
+ }
+
+ public int nextEmptySlot() {
+ ItemStack[] items = getPages().get(1).getContents();
+ for(int i = 0; i < items.length; i++) {
+ ItemStack item = items[i];
+ if(item == null) return i;
+ }
+ return 0;
+ }
+
+ public void addButton(Button button){ if(button != null) if(button.item != null) buttons.add(button); }
+
+ private Inventory createPage(boolean addPageButtons){
+ if(getPages() == null || getPages().isEmpty()) return createPage(1, false);
+ else return createPage(getPages().size() + 1, addPageButtons);
+ }
+
+ private Inventory createPage(int page, boolean addPageButtons){
+ Inventory inv = Bukkit.createInventory(null, getSize(), getTitle());
+ getPages().put(page, inv);
+
+ if(addPageButtons){
+ addPageButtons(inv);
+ }
+
+ return inv;
+ }
+
+ @Deprecated
+ public void hardRefresh(Player player) {
+ this.close(player);
+ this.open(player);
+ }
+
+ @Deprecated
+ public void refresh(Player player) { this.hardRefresh(player); }
+
+ private void addPageButtons(Inventory inv){
+
+ ItemStack nextPageItem = new ItemStack(Material.PAPER);
+ ItemMeta nextMeta = nextPageItem.getItemMeta();
+ nextMeta.setDisplayName("§7Seuraava sivu");
+ nextPageItem.setItemMeta(nextMeta);
+
+ ItemStack prevPageItem = new ItemStack(Material.PAPER);
+ ItemMeta prevMeta = nextPageItem.getItemMeta();
+ prevMeta.setDisplayName("§7Viimeinen sivu");
+ prevPageItem.setItemMeta(prevMeta);
+
+ addButton(new Button(inv, inv.getSize() - 1, nextPageItem){
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ nextPage(clicker);
+ }
+ });
+ addButton(new Button(inv, inv.getSize() - 9, prevPageItem){
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ previousPage(clicker);
+ }
+ });
+ }
+
+ public void addPageButtons(int page){
+ if(getPages() == null || getPages().isEmpty() || getPages().get(page) == null) throw new IllegalArgumentException("You must have at least 1 page in your gui!");
+ Inventory inv = getPages().get(page);
+ addPageButtons(inv);
+ }
+
+ public void removePage(int page){
+ if(getPages() == null || getPages().isEmpty() || getPages().get(page) == null) throw new IllegalArgumentException("You don't have any pages in your inventory or the page you're removing doesn't exist!");
+ getPages().remove(page);
+ }
+
+ public void removeAllPages(){ getPages().clear(); }
+
+ public void open(Player player){
+
+ Bukkit.getScheduler().runTaskAsynchronously(Karhu.getInstance(), () -> {
+ //final long start = System.currentTimeMillis();
+ //System.out.println("[/Gui] Started opening a GUI...");
+ //System.out.println("[/Gui] Checking for pages...");
+ if(getPages() == null || getPages().isEmpty()) throw new IllegalArgumentException("You must have at least 1 page in your gui!");
+
+ //System.out.println("[/Gui] Creating an empty inventory...");
+ this.inv = Bukkit.createInventory(null, this.size, this.title + "§r");
+ if(this.items.isEmpty() && this.buttons.isEmpty()){
+ player.openInventory(inv);
+ return;
+ }
+
+ //System.out.println("[/Gui] Looping given items and adding them...");
+ //final long itemLoopStart = System.currentTimeMillis();
+ HashMap items = this.items.get(1);
+ if(!this.items.isEmpty()) {
+ for(Map.Entry e : items.entrySet()) {
+ inv.setItem(e.getKey(), e.getValue());
+ }
+ }
+ //System.out.println("[/Gui] Item adding took in total " + (System.currentTimeMillis() - itemLoopStart) + "ms!");
+ //System.out.println("[/Gui] Looping given Buttons and adding them...");
+ //final long buttonLoopStart = System.currentTimeMillis();
+ if(!this.buttons.isEmpty()) {
+ for(Button b : this.getButtons()) {
+ inv.setItem(b.pos, b.item);
+ }
+ }
+ //System.out.println("[/Gui] Button adding took in total " + (System.currentTimeMillis() - buttonLoopStart) + "ms!");
+ playerPages.put(player, 1);
+ //System.out.println("[/Gui] Opening the Inventory...");
+ Bukkit.getScheduler().runTask(Karhu.getInstance(),() -> player.openInventory(inv));
+ //System.out.println("[/Gui] Gui opening took in total " + (System.currentTimeMillis() - start) + "ms!");
+ });
+
+ }
+
+ public void addItem(int page, ItemStack item, int pos) {
+
+ if(getPages() == null || getPages().isEmpty()) throw new IllegalArgumentException("You must have at least 1 page in your gui!");
+
+ if(!this.items.containsKey(page)){
+ HashMap> list = new HashMap<>();
+ HashMap items = new HashMap<>();
+ items.put(pos, item);
+ list.put(page, items);
+ this.items.put(page, items);
+ } else {
+ HashMap items = this.items.get(page);
+ if(items.containsKey(pos)) items.replace(pos, item);
+ else items.put(pos, item);
+ this.items.replace(page, items);
+ }
+
+ }
+
+ public Inventory openPage(Player player, int page){
+
+ if(getPages() == null || getPages().isEmpty() || getPages().get(page) == null) throw new IllegalArgumentException("You must have at least 1 page in your gui!");
+
+ Inventory inv = getPages().get(page);
+ player.openInventory(inv);
+ playerPages.put(player, page);
+ return inv;
+
+ }
+
+ private void nextPage(Player player){
+
+ if(getPages() == null || getPages().isEmpty()) throw new IllegalArgumentException("You must have at least 1 page in your gui!");
+
+ if(playerPages.containsKey(player)){
+ int currentPage = playerPages.get(player);
+ if(getPages().size() >= currentPage + 1){
+ Inventory nextPage = getPages().get(currentPage + 1);
+ player.openInventory(nextPage);
+ playerPages.put(player, currentPage + 1);
+ }
+ }
+ }
+
+ private void previousPage(Player player){
+ if(getPages() == null || getPages().isEmpty()) throw new IllegalArgumentException("You must have at least 1 page in your gui!");
+
+ if(playerPages.containsKey(player)){
+ int currentPage = playerPages.get(player);
+ if(currentPage - 1 >= 1){
+ Inventory nextPage = getPages().get(currentPage - 1);
+ player.openInventory(nextPage);
+ playerPages.put(player, currentPage - 1);
+ }
+ }
+ }
+
+ public Button getButton(int pos) {
+ for(Button b : getButtons()) { if(b.pos == pos) return b; }
+ return null;
+ }
+
+ public int getPage(Player player){
+ return playerPages.getOrDefault(player, 0);
+ }
+
+ public void close(Player player) { close(player, true); }
+
+ public void close(Player player, boolean closeInventory) {
+ getPlayerPages().remove(player);
+ if(closeInventory) player.closeInventory();
+ guis.remove(this); // Don't leave the gui hanging around...
+ }
+
+ public Set getButtons() { return buttons; }
+
+ private HashMap getPlayerPages(){ return playerPages; }
+
+ public static void closeCurrent(Player player) {
+ Gui gui = getGui(player);
+ if(gui != null) gui.close(player);
+ }
+
+ public static Gui getGui(Player player){
+ for(Gui gui : Gui.guis){ if(gui.getPlayerPages().containsKey(player)) return gui; }
+ return null;
+ }
+
+ public static void openGui(final Player player,final String title, final int size, final TypedCallback cb) {
+ Gui gui = new Gui(title, size);
+ cb.execute(gui);
+ gui.open(player);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/me/liwk/karhu/gui/ItemUtil.java b/src/main/java/me/liwk/karhu/gui/ItemUtil.java
new file mode 100644
index 0000000..3061d39
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/gui/ItemUtil.java
@@ -0,0 +1,39 @@
+package me.liwk.karhu.gui;
+
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.List;
+
+public class ItemUtil {
+
+ public static ItemStack makeItem(Material mat, int amount, String displayName, List lore) {
+ ItemStack item = new ItemStack(mat);
+ ItemMeta meta = item.getItemMeta();
+ meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', displayName));
+ meta.setLore(lore);
+ item.setAmount(amount);
+ item.setItemMeta(meta);
+ return item;
+ }
+
+ public static ItemStack makeItem(Material mat, int amount, String displayName) {
+ ItemStack item = new ItemStack(mat);
+ ItemMeta meta = item.getItemMeta();
+ meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', displayName));
+ item.setAmount(amount);
+ item.setItemMeta(meta);
+ return item;
+ }
+
+ public static ItemStack makeItem(Material mat, int amount) {
+ ItemStack item = new ItemStack(mat, amount);
+ return item;
+ }
+
+ public static ItemStack makeItem(Material mat) {
+ return new ItemStack(mat);
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/gui/TypedCallback.java b/src/main/java/me/liwk/karhu/gui/TypedCallback.java
new file mode 100644
index 0000000..89ecd92
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/gui/TypedCallback.java
@@ -0,0 +1,9 @@
+package me.liwk.karhu.gui;
+
+@FunctionalInterface
+public interface TypedCallback {
+
+ public void execute(T type);
+
+}
+
diff --git a/src/main/java/me/liwk/karhu/handler/MovementProcessor.java b/src/main/java/me/liwk/karhu/handler/MovementProcessor.java
new file mode 100644
index 0000000..ea79c9a
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/handler/MovementProcessor.java
@@ -0,0 +1,156 @@
+package me.liwk.karhu.handler;
+
+import io.github.retrooper.packetevents.PacketEvents;
+import io.github.retrooper.packetevents.event.PacketListener;
+import io.github.retrooper.packetevents.event.annotation.PacketHandler;
+import io.github.retrooper.packetevents.event.impl.PacketReceiveEvent;
+import io.github.retrooper.packetevents.packettype.PacketType;
+import io.github.retrooper.packetevents.utils.server.ServerVersion;
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.data.PlayerData;
+import me.liwk.karhu.playerhandler.Handler1_13;
+import me.liwk.karhu.playerhandler.Handler1_8;
+import me.liwk.karhu.playerhandler.Handler1_9;
+import me.liwk.karhu.util.UtilPlayer;
+import org.bukkit.entity.Player;
+
+public class MovementProcessor implements PacketListener {
+
+ @PacketHandler
+ public void onPacketReceive(PacketReceiveEvent e) {
+ final Player player = e.getPlayer();
+ PlayerData playerData = null;
+
+ if (player != null) {
+ playerData = Karhu.getInstance().getPlayerDataManager().getPlayerData(e.getPlayer());
+ }
+
+ if (playerData != null && playerData.isObjectLoaded()) {
+
+ if (PacketType.Client.Util.isInstanceOfFlying(e.getPacketId())) {
+
+ if (playerData.getLastLocation() != null && playerData.getLastLastLocation() != null) {
+
+ playerData.setWasOnGround(playerData.isOnGround());
+ playerData.setWasInLiquid(playerData.isInLiquid());
+ playerData.setWasUnderBlock(playerData.isUnderBlock());
+ playerData.setWasInWeb(playerData.isInWeb());
+ playerData.setInLiquid(UtilPlayer.isNearLiquid(player));
+ playerData.setInWeb(UtilPlayer.isInWeb(player));
+ playerData.setOnIce(UtilPlayer.isNearIce(player));
+
+ if (PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_8)) {
+ if (Handler1_9.isGliding(player)) {
+
+ }
+ } else {
+ if (Handler1_8.isGliding(player)) {
+
+ }
+ }
+
+ if (PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_12_2)) {
+ if (Handler1_13.isRiptiding(player)) {
+
+ }
+ }
+
+ if (playerData.isOnIce()) {
+ playerData.setMovementsSinceIce(0);
+ } else {
+ playerData.setMovementsSinceIce(playerData.getMovementsSinceIce() + 1);
+ }
+
+ if (playerData.isOnGround()) {
+ playerData.setLastGroundY(playerData.getLocation().getY());
+ }
+
+ playerData.setOnStairs(UtilPlayer.isOnStair(player));
+ playerData.setUnderBlock(UtilPlayer.blockNearHead(player));
+ if (playerData.isUnderBlock()) {
+ playerData.setMovementsSinceUnderBlock(0);
+ } else {
+ playerData.setMovementsSinceUnderBlock(playerData.getMovementsSinceUnderBlock() + 1);
+ }
+
+ if (playerData.getVelocityH() > 0) {
+ playerData.setVelocityH(playerData.getVelocityH() - 1);
+ } else {
+ if (playerData.getVelocityIds().size() == 0) playerData.setVelocityHorizontal(0);
+ }
+
+ if (playerData.getVelocityV() > 0) {
+ playerData.setVelocityV(playerData.getVelocityV() - 1);
+ } else {
+ if (playerData.getVelocityIds().size() == 0) playerData.setVelocityY(0);
+ }
+
+ double x2 = Math.floor(playerData.getLastLocation().getX());
+ double z2 = Math.floor(playerData.getLastLocation().getZ());
+
+ if (Math.floor(playerData.getLocation().getX()) != x2 || Math.floor(playerData.getLocation().getZ()) != z2) {
+ playerData.setLastFullblockMoved(System.currentTimeMillis());
+ }
+
+ if (UtilPlayer.isClimbableBlock(player)) {
+ playerData.setClimbableTicks(playerData.getClimbableTicks() + 1);
+ } else {
+ playerData.setClimbableTicks(0);
+ }
+
+ boolean onGroundReal = playerData.isOnGround();
+
+ if (onGroundReal) {
+
+ playerData.setAirTicks(0);
+ playerData.setGroundTicks(playerData.getGroundTicks() + 1);
+
+ if (playerData.getAboveBlockTicks() < 60) {
+ playerData.setAboveBlockTicks(playerData.getAboveBlockTicks() + 1);
+ }
+ } else {
+ playerData.setAirTicks(playerData.getAirTicks() + 1);
+ playerData.setGroundTicks(0);
+
+ if (playerData.getAboveBlockTicks() > 0) {
+ playerData.setAboveBlockTicks(playerData.getAboveBlockTicks() - 1);
+ }
+
+ }
+
+ if(playerData.isOnGround()) {
+ playerData.setClientAirTicks(0);
+ } else {
+ playerData.setClientAirTicks(playerData.getClientAirTicks() + 1);
+ }
+
+ if (playerData.isOnGroundPacket() && playerData.isLastOnGroundPacket() && playerData.fallY != -690000) {
+ playerData.fallDistance = Math.abs(playerData.fallY - playerData.getLocation().getY());
+ playerData.fallY = -690000;
+ }
+
+ if (!playerData.isOnGroundPacket() && playerData.isLastOnGroundPacket()) {
+ playerData.fallY = playerData.getLocation().getY();
+ }
+
+ playerData.liquidTicks = Math.max(0, UtilPlayer.isNearLiquid(player) ? Math.min(80, playerData.liquidTicks + 10) : playerData.liquidTicks - 1);
+ playerData.blockTicks = Math.max(0, playerData.isOnGround() ? Math.min(60, playerData.blockTicks + 3) : playerData.blockTicks - 1);
+ playerData.slimeTicks = Math.max(0, UtilPlayer.isNearSlime(player.getLocation()) ? Math.min(playerData.slimeTicks + 60, 120) : playerData.slimeTicks - 1);
+ playerData.slimeHeight = playerData.getSlimeTicks() > 0 ? ((Math.abs(playerData.fallDistance) * 0.05) * playerData.getSlimeTicks() * 0.05f) * 4 : 0;
+ playerData.iceTicks = Math.max(0, UtilPlayer.isNearIce(player) ? Math.min(60, playerData.iceTicks + 5) : playerData.iceTicks - 1);
+ playerData.halfTicks = Math.max(0, UtilPlayer.isOnStair2(player) || UtilPlayer.isOnSlab(player) ? Math.min(60, playerData.halfTicks + 5) : playerData.halfTicks - 1);
+ playerData.skullTicks = Math.max(0, UtilPlayer.isOnSkull(player) ? Math.min(60, playerData.skullTicks + 5) : playerData.skullTicks - 1);
+ playerData.potTicks = Math.max(0, UtilPlayer.isOnPot(player) ? Math.min(20, playerData.potTicks + 5) : playerData.potTicks - 1);
+ playerData.wallTicks = Math.max(0, UtilPlayer.isOnWall(player) || UtilPlayer.isOnFence(player) ? Math.min(60, playerData.wallTicks + 5) : playerData.wallTicks - 1);
+
+ if (playerData.isOnSlime()) {
+ playerData.setLastOnSlime(System.currentTimeMillis());
+ }
+
+ playerData.setBelowBlock(false);
+ playerData.setWasBelowBlock(false);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/liwk/karhu/handler/PacketProcessor.java b/src/main/java/me/liwk/karhu/handler/PacketProcessor.java
new file mode 100644
index 0000000..f46a354
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/handler/PacketProcessor.java
@@ -0,0 +1,494 @@
+package me.liwk.karhu.handler;
+
+
+import io.github.retrooper.packetevents.PacketEvents;
+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.impl.PacketReceiveEvent;
+import io.github.retrooper.packetevents.event.impl.PacketSendEvent;
+import io.github.retrooper.packetevents.packettype.PacketType;
+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.flying.WrappedPacketInFlying;
+import io.github.retrooper.packetevents.packetwrappers.in.helditemslot.WrappedPacketInHeldItemSlot;
+import io.github.retrooper.packetevents.packetwrappers.in.transaction.WrappedPacketInTransaction;
+import io.github.retrooper.packetevents.packetwrappers.in.useentity.WrappedPacketInUseEntity;
+import io.github.retrooper.packetevents.packetwrappers.out.entityvelocity.WrappedPacketOutEntityVelocity;
+import io.github.retrooper.packetevents.packetwrappers.out.position.WrappedPacketOutPosition;
+import io.github.retrooper.packetevents.packetwrappers.out.transaction.WrappedPacketOutTransaction;
+import io.github.retrooper.packetevents.utils.player.ClientVersion;
+import io.github.retrooper.packetevents.utils.server.ServerVersion;
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.check.api.Check;
+import me.liwk.karhu.data.PlayerData;
+import me.liwk.karhu.event.*;
+import me.liwk.karhu.playerhandler.VersionBridgeHelper;
+import me.liwk.karhu.util.*;
+import me.liwk.karhu.util.check.EnabledUtil;
+import me.liwk.karhu.util.pair.Pair;
+import me.liwk.karhu.util.task.Tasker;
+import org.bukkit.Location;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.util.Vector;
+
+public class PacketProcessor extends MathUtil implements PacketListener {
+
+ @PacketHandler
+ public void onPacketReceive(PacketReceiveEvent e) {
+ final Player p = e.getPlayer();
+ PlayerData playerData = null;
+
+ if (p != null) {
+ playerData = Karhu.getInstance().getPlayerDataManager().getPlayerData(e.getPlayer());
+ }
+
+ if (playerData != null && playerData.isObjectLoaded()) {
+ final long timestamp = e.getTimestamp();
+ PacketEvent callEvent = e;
+ if (PacketType.Client.Util.isInstanceOfFlying(e.getPacketId())) {
+ final WrappedPacketInFlying flying = new WrappedPacketInFlying(e.getNMSPacket());
+ long packetDiff = System.currentTimeMillis() - playerData.getLastFlying();
+ long now = System.currentTimeMillis();
+ int nowTicks = playerData.getTotalTicks();
+ double x = flying.getX();
+ double y = flying.getY();
+ double z = flying.getZ();
+ float yaw = flying.getYaw();
+ float pitch = flying.getPitch();
+ boolean onGround = flying.isOnGround();
+
+ playerData.lagTick = packetDiff < 2L;
+
+ boolean delayed = nowTicks - playerData.lastFlyingTicks > 2;
+
+ final boolean lagging = nowTicks - playerData.lastDroppedPackets < 2;
+
+ playerData.setDroppedPackets(lagging);
+
+ if (packetDiff > 150L) {
+ playerData.lastLag = now;
+ }
+
+ if (packetDiff > 300L) {
+ playerData.lastLag2 = now;
+ }
+
+ if (packetDiff > 100L) {
+ playerData.setLastDelayed(now);
+ }
+
+ if (packetDiff < 25L) {
+ playerData.setLastFast(now);
+ }
+
+ if (now - playerData.getLastFlying() >= 110L) {
+ playerData.setLastDelayedPacket(now);
+ }
+
+ if (flying.isPosition()) {
+ playerData.setLastMovePkt(now);
+ }
+
+ if (flying.isLook()) {
+ playerData.setLastMovement(now);
+ }
+
+ playerData.totalTicks++;
+ playerData.lastAttackTick++;
+ playerData.lastServerPositionTick++;
+
+ Location location = null;
+
+ if (flying.isPosition()) {
+ if (playerData.getLocation() != null) {
+ location = new Location(e.getPlayer().getWorld(), x, y, z, playerData.getLocation().getYaw(), playerData.getLocation().getPitch());
+ Location lastLocation = (playerData.getLocation() != null) ? playerData.getLocation() : location;
+ playerData.setLastLastLastLocation(playerData.getLastLastLocation());
+ playerData.setLastLastLocation(playerData.getLastLocation());
+ playerData.setLastLocation(lastLocation);
+ playerData.setLocation(location);
+ playerData.positionTicks++;
+ callEvent = new FlyingEvent(x, y, z, playerData.getLocation().getYaw(), playerData.getLocation().getPitch(),
+ flying.isPosition(),
+ flying.isLook(),
+ onGround,
+ p.getWorld());
+ } else {
+ location = new Location(e.getPlayer().getWorld(), x, y, z, p.getLocation().getYaw(), p.getLocation().getPitch());
+ Location lastLocation = (playerData.getLocation() != null) ? playerData.getLocation() : location;
+ playerData.setLastLastLastLocation(playerData.getLastLastLocation());
+ playerData.setLastLastLocation(playerData.getLastLocation());
+ playerData.setLastLocation(lastLocation);
+ playerData.setLocation(location);
+ playerData.positionTicks++;
+ callEvent = new FlyingEvent(x, y, z, playerData.getLocation().getYaw(), playerData.getLocation().getPitch(),
+ flying.isPosition(),
+ flying.isLook(),
+ onGround,
+ p.getWorld());
+ }
+ } else if (flying.isLook()) {
+ if (playerData.getLocation() != null) {
+ location = new Location(p.getWorld(), playerData.getLocation().getX(), playerData.getLocation().getY(), playerData.getLocation().getZ(), flying.getYaw(), flying.getPitch());
+ Location lastLocation = playerData.getLocation() != null ? playerData.getLocation() : location;
+ playerData.setLastLastLastLocation(playerData.getLastLastLocation());
+ playerData.setLastLastLocation(playerData.getLastLocation());
+ playerData.setLastLocation(lastLocation);
+ playerData.setLocation(location);
+ callEvent = new FlyingEvent(playerData.getLocation().getX(), playerData.getLocation().getY(), playerData.getLocation().getZ(), flying.getYaw(), flying.getPitch(),
+ flying.isPosition(),
+ flying.isLook(),
+ onGround,
+ p.getWorld());
+ } else {
+ location = new Location(p.getWorld(), p.getLocation().getX(), p.getLocation().getY(), p.getLocation().getZ(), flying.getYaw(), flying.getPitch());
+ Location lastLocation = playerData.getLocation() != null ? playerData.getLocation() : location;
+ playerData.setLastLastLastLocation(playerData.getLastLastLocation());
+ playerData.setLastLastLocation(playerData.getLastLocation());
+ playerData.setLastLocation(lastLocation);
+ playerData.setLocation(location);
+ callEvent = new FlyingEvent(p.getLocation().getX(), p.getLocation().getY(), p.getLocation().getZ(), flying.getYaw(), flying.getPitch(),
+ flying.isPosition(),
+ flying.isLook(),
+ onGround,
+ p.getWorld());
+ }
+ }
+ if (!flying.isLook() && !flying.isPosition()) {
+ if (playerData.getLocation() != null) {
+ callEvent = new FlyingEvent(playerData.getLocation().getX(), playerData.getLocation().getY(), playerData.getLocation().getZ(), playerData.getLocation().getYaw(), playerData.getLocation().getPitch(),
+ flying.isPosition(),
+ flying.isLook(),
+ onGround,
+ p.getWorld());
+ }
+ }
+
+ if (flying.isPosition() && playerData.teleportReset) {
+ playerData.teleportReset = false;
+ playerData.setTeleportLocation(null);
+ playerData.lastServerPositionTick = 600;
+ playerData.lastTeleportReset = playerData.getTotalTicks();
+ }
+
+ if (flying.isPosition() && playerData.getTeleportLocation() != null) {
+ double xTP = Math.abs(playerData.getLocation().getX() - playerData.getTeleportLocation().getX());
+ double yTP = Math.abs(playerData.getLocation().getY() - playerData.getTeleportLocation().getY());
+ double zTP = Math.abs(playerData.getLocation().getZ() - playerData.getTeleportLocation().getZ());
+ if (xTP <= 0.005 || yTP <= 0.005 || zTP <= 0.005 && playerData.getTotalTicks() > 100) {
+ playerData.teleportReset = true;
+ }
+ }
+
+ playerData.setLastLastOnGroundPacket(playerData.isLastOnGroundPacket());
+ playerData.setLastOnGroundPacket(playerData.isOnGroundPacket());
+ playerData.setOnGroundPacket(onGround);
+ playerData.setLastOnGround(playerData.isOnGround());
+ playerData.setOnGround(UtilPlayer.isOnGroundBB(p));
+
+ if (Karhu.getInstance().getFileManager().isTransaction()) {
+ if (playerData.getTransPing() > 40000) {
+ Tasker.run(() -> {
+ p.kickPlayer("Internal Exception : java.io.IOExpection : An existing Connection was forcibly closed by the remote host.");
+ });
+ }
+ }
+
+ if (lagging) playerData.setLastPacketDrop(playerData.getTotalTicks());
+
+
+ if (p.isFlying()) {
+ playerData.setLastFlyTick(playerData.getTotalTicks());
+ }
+
+ Player target = playerData.getLastTarget();
+
+ if (target != null && target.isOnline()) {
+
+ PlayerData targetData = Karhu.getInstance().getPlayerDataManager().getPlayerData(target);
+
+ if (targetData == null) return;
+
+ double x1 = (int) (targetData.getLocation().getX() * 32.0D);
+ double y1 = (int) (targetData.getLocation().getY() * 32.0D);
+ double z1 = (int) (targetData.getLocation().getZ() * 32.0D);
+
+ double x2 = x1 / 32.0D;
+ double y2 = y1 / 32.0D;
+ double z2 = z1 / 32.0D;
+
+ AxisAlignedBB box = getEntityBoundingBox(x2, y2, z2);
+ AxisAlignedBB box2 = getEntityBoundingBox(x2, y2, z2);
+
+ AxisAlignedBB axisalignedbb;
+ AxisAlignedBB axisalignedbb2;
+
+ if (playerData.getClientVersion().isHigherThan(ClientVersion.v_1_8)) {
+ axisalignedbb = box.expand(0.03, 0.03, 0.03);
+ } else if (playerData.lastPosition > 0) {
+ axisalignedbb = box.expand(0.13, 0.13, 0.13);
+ } else {
+ axisalignedbb = box.expand(0.1, 0.1, 0.1);
+ }
+
+ if (playerData.getClientVersion().isHigherThan(ClientVersion.v_1_8)) {
+ axisalignedbb2 = box2.expand(0.23, 0.23, 0.23);
+ } else if (playerData.lastPosition2 > 0) {
+ axisalignedbb2 = box.expand(0.33, 0.33, 0.33);
+ } else {
+ axisalignedbb2 = box2.expand(0.3, 0.3, 0.3);
+ }
+ if (!playerData.isTeleporting() && !p.isInsideVehicle() && !target.isInsideVehicle()) {
+ playerData.getPastLocs().add(new Pair<>(axisalignedbb, playerData.getTotalTicks()));
+ playerData.getPastLocations().add(new Pair<>(axisalignedbb, playerData.getTotalTicks()));
+ playerData.getPastLocsHitBox().add(new Pair<>(axisalignedbb2, playerData.getTotalTicks()));
+ } else {
+ playerData.getPastLocs().clear();
+ playerData.getPastLocations().clear();
+ playerData.getPastLocsHitBox().clear();
+ }
+ }
+
+ if (playerData.getLocation() != null && playerData.getLastLocation() != null) {
+
+ float disX = (float) ((float) playerData.getLocation().getX() - playerData.getLastLocation().getX());
+ float disZ = (float) ((float) playerData.getLocation().getZ() - playerData.getLastLocation().getZ());
+
+ float disXZ = disX * disX + disZ * disZ;
+ float deltaXZ = (float) Math.sqrt(disXZ);
+ float deltaXZ2 = (float) MathUtil.hypot(playerData.getLocation().getX() - playerData.getLastLocation().getX(), playerData.getLocation().getZ() - playerData.getLastLocation().getZ());
+
+ playerData.setDeltaX(disX);
+ playerData.setDeltaZ(disZ);
+
+ playerData.setDeltaX(disX);
+ playerData.setDeltaZ(disZ);
+
+ playerData.setLastDeltaXZ(playerData.getDeltaXZ());
+ playerData.setDeltaXZ(deltaXZ);
+ playerData.setDeltaXZ2(deltaXZ2);
+
+ playerData.setLastDeltaY(playerData.getDeltaY());
+ //double deltaMCP = playerData.getLocation().getY() - playerData.getLastLocation().getY() >= 0.005 ? playerData.getLocation().getY() - playerData.getLastLocation().getY() : 0.0;
+ playerData.setDeltaY((float) (playerData.getLocation().getY() - playerData.getLastLocation().getY()));
+ }
+
+ playerData.setPlacing(false);
+
+ playerData.lastDroppedPackets = delayed ? nowTicks : playerData.lastDroppedPackets;
+ playerData.setLastFlying(now);
+ playerData.setLastFlyingTicks(nowTicks);
+ playerData.setInventoryOpen(false);
+ } else if (e.getPacketId() == PacketType.Client.KEEP_ALIVE) {
+ playerData.setLastKeepAlive(System.currentTimeMillis());
+ playerData.setLastPing(playerData.getPing());
+ playerData.setPing((int) Math.abs(System.currentTimeMillis() - playerData.getLastServerKeepAlive()));
+ } else if (e.getPacketId() == PacketType.Client.USE_ENTITY) {
+ final WrappedPacketInUseEntity use = new WrappedPacketInUseEntity(e.getNMSPacket());
+ if (use.getAction().equals(WrappedPacketInUseEntity.EntityUseAction.ATTACK)) {
+ callEvent = new AttackEvent(use.getEntityID(), use.getEntity());
+ if (!playerData.isAttackedSinceVelocity()) {
+ playerData.setVelocityX(playerData.getVelocityX() * 0.6);
+ playerData.setVelocityZ(playerData.getVelocityZ() * 0.6);
+ playerData.setAttackedSinceVelocity(true);
+ }
+ playerData.lastAttackTick = 0;
+ playerData.setLastAttackPacket(System.currentTimeMillis());
+ if (use.getEntity() instanceof Player) {
+ Player target = (Player) use.getEntity();
+ if (playerData.getLastTarget() != null) {
+ playerData.setLastLastTarget(playerData.getLastTarget());
+ if (use.getEntity() != playerData.getLastTarget()) {
+ playerData.getPastLocs().clear();
+ playerData.getPastLocations().clear();
+ playerData.getPastLocsHitBox().clear();
+ }
+ }
+ playerData.setLastTarget(target);
+ playerData.target.set(target);
+ } else {
+ playerData.setLastTarget(null);
+ playerData.target.set(null);
+ }
+ } else if (use.getAction() == WrappedPacketInUseEntity.EntityUseAction.INTERACT || use.getAction() == WrappedPacketInUseEntity.EntityUseAction.INTERACT_AT) {
+ if (use.getEntity() instanceof LivingEntity) {
+ callEvent = new InteractEvent(use.getEntityID(), use.getEntity());
+ }
+ }
+ } else if (e.getPacketId() == PacketType.Client.ARM_ANIMATION) {
+ callEvent = new SwingEvent();
+ } else if (e.getPacketId() == PacketType.Client.WINDOW_CLICK) {
+ callEvent = new WindowEvent();
+ } else if (e.getPacketId() == PacketType.Client.CLIENT_COMMAND) {
+ final WrappedPacketInClientCommand clientCommand = new WrappedPacketInClientCommand(e.getNMSPacket());
+ callEvent = new ClientCommandEvent(clientCommand.getClientCommand());
+ if (clientCommand.getClientCommand().equals(WrappedPacketInClientCommand.ClientCommand.OPEN_INVENTORY_ACHIEVEMENT)) {
+ playerData.setInventoryOpen(true);
+ playerData.setInvStamp(playerData.getTotalTicks());
+ }
+ } else if (e.getPacketId() == PacketType.Client.BLOCK_DIG) {
+ final WrappedPacketInBlockDig blockDig = new WrappedPacketInBlockDig(e.getNMSPacket());
+ if (blockDig.getDigType() == WrappedPacketInBlockDig.PlayerDigType.START_DESTROY_BLOCK) {
+ playerData.setDigging(true);
+ playerData.setLastDig(playerData.getTotalTicks());
+ } else if (blockDig.getDigType() == WrappedPacketInBlockDig.PlayerDigType.ABORT_DESTROY_BLOCK || blockDig.getDigType() == WrappedPacketInBlockDig.PlayerDigType.STOP_DESTROY_BLOCK) {
+ playerData.setDigging(false);
+ }
+ callEvent = new DigEvent(new Vector(blockDig.getX(), blockDig.getY(), blockDig.getZ()), blockDig.getDigType());
+ } else if (e.getPacketId() == PacketType.Client.ENTITY_ACTION) {
+ final WrappedPacketInEntityAction action = new WrappedPacketInEntityAction(e.getNMSPacket());
+
+ switch (action.getAction()) {
+ case START_SPRINTING:
+ playerData.setSprinting(true);
+ break;
+ case STOP_SPRINTING:
+ playerData.setSprinting(false);
+ break;
+ case START_SNEAKING:
+ playerData.setSneaking(true);
+ break;
+ case STOP_SNEAKING:
+ playerData.setSneaking(false);
+ break;
+ }
+
+ } else if ((e.getPacketId() == PacketType.Client.BLOCK_PLACE && Karhu.SERVER_VERSION.isLowerThan(ServerVersion.v_1_9)) || (e.getPacketId() == PacketType.Client.USE_ITEM && Karhu.SERVER_VERSION.isHigherThan(ServerVersion.v_1_8_8))) {
+ final WrappedPacketInBlockPlace packet = new WrappedPacketInBlockPlace(e.getNMSPacket());
+ final ItemStack stack = VersionBridgeHelper.getStackInHand(playerData);
+ playerData.setPlacing(true);
+ callEvent = new BlockPlaceEvent(new Vector(packet.getX(), packet.getY(), packet.getZ()), stack);
+ } else if (e.getPacketId() == PacketType.Client.HELD_ITEM_SLOT) {
+ final WrappedPacketInHeldItemSlot packet = new WrappedPacketInHeldItemSlot(e.getNMSPacket());
+ callEvent = new HeldItemSlotEvent(packet.getCurrentSelectedSlot());
+ } else if (e.getPacketId() == PacketType.Client.ABILITIES) {
+ callEvent = new AbilityEvent();
+ } else if (e.getPacketId() == PacketType.Client.STEER_VEHICLE) {
+ playerData.setLastVehicle(System.currentTimeMillis());
+ } else if (e.getPacketId() == PacketType.Client.TRANSACTION) {
+ final WrappedPacketInTransaction packet = new WrappedPacketInTransaction(e.getNMSPacket());
+ final long now = System.currentTimeMillis();
+ if (playerData.getVelocityIds().containsKey(packet.getActionNumber())) {
+ //Vector
+ Vector velocity = playerData.getVelocityIds().get(packet.getActionNumber());
+
+ //Ticks
+ playerData.setVelocityH((int) (((velocity.getX() + velocity.getZ()) / 2.0D + 2.0D) * 15.0D));
+ playerData.setVelocityV((int) (Math.pow(velocity.getY() + 2.0D, 2.0D) * 5.0D));
+
+ //Velocities
+ playerData.setLastVelocityX(playerData.getVelocityX());
+ playerData.setLastVelocityY(playerData.getVelocityY());
+ playerData.setLastVelocityZ(playerData.getVelocityZ());
+
+ playerData.setVelocityX(velocity.getX());
+ playerData.setVelocityY(velocity.getY());
+ playerData.setVelocityZ(velocity.getZ());
+
+ playerData.setVelocityTicks(playerData.getTotalTicks());
+
+ playerData.setVelocityHorizontal(Math.hypot(velocity.getX(), velocity.getZ()));
+
+ //Remove processed id to prevent weird stuff / mem leaks
+ playerData.getVelocityIds().remove(packet.getActionNumber());
+ } else if (playerData.getTransactionTime().containsKey(packet.getActionNumber())) {
+
+ //Map
+ long transactionStamp = playerData.getTransactionTime().get(packet.getActionNumber());
+ playerData.setTimerTransactionReceived(playerData.getTimerTransactionReceived() + 1);
+
+ //Ping
+ playerData.setTransPing(now - transactionStamp);
+
+ //Remove processed id to prevent weird stuff / mem leaks
+ playerData.getTransactionSentMap().remove(packet.getActionNumber());
+ }
+ } else if (e.getPacketId() == PacketType.Client.CUSTOM_PAYLOAD) {
+
+ final WrappedPacketInCustomPayload packet = new WrappedPacketInCustomPayload(e.getNMSPacket());
+
+ if (packet.getTag() != null) {
+
+ if (packet.getTag().equals("MC|Brand")) {
+ String brand = new String(packet.getData());
+ playerData.setBrand(brand);
+
+ }
+
+ }
+ }
+
+ PacketEvent finalCallEvent = callEvent;
+ if (playerData.getCheckManager().getChecks() != null) {
+ for (Check check : playerData.getCheckManager().getChecks()) {
+ if (EnabledUtil.checkIfIsEnabled(check.getName().replace(" ", ""))) {
+ check.handle(finalCallEvent, p);
+ }
+ }
+ }
+ }
+ }
+
+ @PacketHandler
+ public void onPacketSend(PacketSendEvent e) {
+ final Player p = e.getPlayer();
+ PlayerData playerData = null;
+
+ if (p != null) {
+ playerData = Karhu.getInstance().getPlayerDataManager().getPlayerData(e.getPlayer());
+ }
+
+ if (playerData != null && playerData.isObjectLoaded()) {
+ if (e.getPacketId() == PacketType.Server.KEEP_ALIVE) {
+ playerData.setLastServerKeepAlive(e.getTimestamp());
+ } else if (e.getPacketId() == PacketType.Server.POSITION) {
+ final WrappedPacketOutPosition packet = new WrappedPacketOutPosition(e.getNMSPacket());
+ if (playerData.getCheckManager().getChecks() != null) {
+ for (Check check : playerData.getCheckManager().getChecks()) {
+ if (EnabledUtil.checkIfIsEnabled(check.getName().replace(" ", ""))) {
+ check.handle(new PositionEvent(-1, -1, -1, -1, -1), e.getPlayer());
+ }
+ }
+ }
+ playerData.lastServerPositionTick = 0;
+ playerData.setTeleportLocation(new CustomLocation(packet.getX(), packet.getY(), packet.getZ(), 0F, 0F));
+ playerData.setLastTeleport(System.currentTimeMillis());
+ } else if (e.getPacketId() == PacketType.Server.OPEN_WINDOW) {
+ playerData.setInventoryOpen(true);
+ playerData.setInvStamp(playerData.getTotalTicks());
+ } else if (e.getPacketId() == PacketType.Server.ENTITY_VELOCITY) {
+ final WrappedPacketOutEntityVelocity velocity = new WrappedPacketOutEntityVelocity(e.getNMSPacket());
+ if (velocity.getEntityId() == p.getEntityId()) {
+ playerData.setVelocityBeingConfirmed(true);
+
+ RandomShort random = new RandomShort();
+ short randomID = random.nextShort();
+ playerData.getVelocityIds().put((short) -(randomID), new Vector(velocity.getVelocityX(), velocity.getVelocityY(), velocity.getVelocityZ()));
+
+ Tasker.run(() -> {
+ PacketEvents.getAPI().getPlayerUtils().sendPacket(e.getPlayer(), new WrappedPacketOutTransaction(0, (short) -(randomID), false));
+ });
+ }
+ } else if (e.getPacketId() == PacketType.Server.TRANSACTION) {
+
+ final WrappedPacketOutTransaction transaction = new WrappedPacketOutTransaction(e.getNMSPacket());
+
+ playerData.getTransactionSentMap().put(transaction.getActionNumber(), System.currentTimeMillis());
+
+ }
+ }
+ }
+
+ public static AxisAlignedBB getEntityBoundingBox(double x, double y, double z) {
+ float f = 0.6F / 2.0F;
+ float f1 = 1.8F;
+ return (new AxisAlignedBB(x - (double)f, y, z - (double)f, x + (double)f, y + (double)f1, z + (double)f));
+ }
+}
+
diff --git a/src/main/java/me/liwk/karhu/listener/PlayerListener.java b/src/main/java/me/liwk/karhu/listener/PlayerListener.java
new file mode 100644
index 0000000..a5db9da
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/listener/PlayerListener.java
@@ -0,0 +1,133 @@
+package me.liwk.karhu.listener;
+
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.data.PlayerData;
+import me.liwk.karhu.gui.Button;
+import me.liwk.karhu.gui.Gui;
+import me.liwk.karhu.util.update.UpdateCheck;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.vehicle.VehicleEnterEvent;
+
+import java.util.concurrent.ForkJoinPool;
+
+public class PlayerListener implements Listener {
+ private final Karhu plugin;
+
+ @EventHandler
+ public void onVehicle(VehicleEnterEvent e) {
+
+ if(e.getEntered() instanceof Player) {
+
+ final Player player = (Player) e.getEntered();
+
+ final PlayerData playerData = Karhu.getInstance().getPlayerDataManager().getPlayerData(player);
+
+ playerData.setLastVehicleTicks(playerData.getTotalTicks());
+ }
+
+
+ }
+
+ @EventHandler(priority = EventPriority.HIGHEST)
+ public void onJoin(PlayerJoinEvent e) {
+
+ final Player player = e.getPlayer();
+
+ ForkJoinPool.commonPool().execute(() -> Karhu.getInstance().getPlayerDataManager().add(e.getPlayer()));
+
+ if (player.hasPermission("karhu.alerts")) {
+ Karhu.getInstance().getAlertsManager().toggleAlerts(player);
+ }
+
+
+ if(UpdateCheck.getNewVersion() != null) {
+ if (!Karhu.getInstance().getBuild().equals(UpdateCheck.getNewVersion())) {
+ if(player.hasPermission("karhu.staff")) UpdateCheck.informStaff(player);
+ }
+ }
+ }
+
+ private String dogshit24(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+ @EventHandler
+ public void onQuit(PlayerQuitEvent e) {
+
+ if (Karhu.getInstance().getAlertsManager().hasAlertsToggled(e.getPlayer())) {
+ Karhu.getInstance().getAlertsManager().toggleAlerts(e.getPlayer());
+ }
+
+ ForkJoinPool.commonPool().execute(() -> Karhu.getInstance().getPlayerDataManager().remove(e.getPlayer()));
+ }
+
+ private String dogshit13(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+ @EventHandler
+ public void onOpen(PlayerInteractEvent event) {
+ final PlayerData playerData = Karhu.getInstance().getPlayerDataManager().getPlayerData(event.getPlayer());
+ if (event.getAction().equals(Action.RIGHT_CLICK_BLOCK)
+ && event.getClickedBlock() != null
+ && !event.isCancelled()
+ && (event.getClickedBlock().getType().name().contains("DOOR") || event.getClickedBlock().getType().name().contains("FENCE"))) {
+ playerData.setLastOpeningInteract(playerData.getTotalTicks());
+ }
+ }
+
+
+ public PlayerListener(Karhu plugin) {
+ this.plugin = plugin;
+ }
+
+ private String dogshit11(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+ @EventHandler
+ public void onInvClick(InventoryClickEvent e) {
+ if(e.getClickedInventory() == null) return;
+ if(e.getCurrentItem() == null) return;
+ if(e.getWhoClicked().getOpenInventory().getTitle().contains("§r")) e.setCancelled(true);
+ Player player = (Player) e.getWhoClicked();
+ if(Gui.getGui(player) != null) {
+ Gui gui = Gui.getGui(player);
+ if(e.getCurrentItem() != null) {
+ for(Button b : gui.getButtons()) {
+ if(b.item.clone().equals(e.getCurrentItem())) {
+ e.setCancelled(true);
+ b.onClick(player, e.getClick());
+ }
+ }
+ }
+ }
+ }
+
+ private String dogshi1(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+ @EventHandler(priority = EventPriority.HIGHEST)
+ public void onInvClose(InventoryCloseEvent e) {
+
+ Player player = (Player) e.getPlayer();
+ if(e.getView().getTitle().contains("§r") && Gui.getGui(player) != null) {
+ Gui.getGui(player).close(player);
+ }
+
+ }
+
+ private String dogshit(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/manager/AlertsManager.java b/src/main/java/me/liwk/karhu/manager/AlertsManager.java
new file mode 100644
index 0000000..8d61a23
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/manager/AlertsManager.java
@@ -0,0 +1,50 @@
+package me.liwk.karhu.manager;
+
+import org.bukkit.entity.Player;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+public class AlertsManager {
+ private final Set devAlertsToggled;
+ private final Set alertsToggled;
+
+ public boolean hasAlertsToggled(Player player) {
+ return this.alertsToggled.contains(player.getUniqueId());
+ }
+
+ public boolean hasDevAlertsToggled(Player player) {
+ return this.devAlertsToggled.contains(player.getUniqueId());
+ }
+
+
+ public void toggleAlerts(Player player) {
+ if (!this.alertsToggled.remove(player.getUniqueId())) {
+ this.alertsToggled.add(player.getUniqueId());
+ }
+ }
+
+ private String dogshit(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+ public void toggleDevAlerts(Player player) {
+ if (!this.devAlertsToggled.remove(player.getUniqueId())) {
+ this.devAlertsToggled.add(player.getUniqueId());
+ }
+ }
+
+ public AlertsManager() {
+ this.alertsToggled = new HashSet<>();
+ this.devAlertsToggled = new HashSet<>();
+ }
+
+ public Set getAlertsToggled() {
+ return this.alertsToggled;
+ }
+
+ public Set getDevAlertsToggled() {
+ return devAlertsToggled;
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/manager/PlayerDataManager.java b/src/main/java/me/liwk/karhu/manager/PlayerDataManager.java
new file mode 100644
index 0000000..1795f25
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/manager/PlayerDataManager.java
@@ -0,0 +1,36 @@
+package me.liwk.karhu.manager;
+
+import lombok.Getter;
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.data.PlayerData;
+import org.bukkit.entity.Player;
+
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public final class PlayerDataManager {
+
+ @Getter
+ private final Map playerDataMap = Collections.synchronizedMap(new IdentityHashMap<>());
+
+ private final Karhu karhu;
+
+ public PlayerDataManager(Karhu karhu) {
+ this.karhu = karhu;
+ }
+
+ public PlayerData getPlayerData(final Player player) {
+ return playerDataMap.get(player.getUniqueId());
+ }
+
+ public PlayerData remove(final Player player) {
+ return playerDataMap.remove(player.getUniqueId());
+ }
+
+ public void add(final Player player) {
+ this.playerDataMap.put(player.getUniqueId(), new PlayerData(player, karhu));
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/me/liwk/karhu/menu/ChecksMenu.java b/src/main/java/me/liwk/karhu/menu/ChecksMenu.java
new file mode 100644
index 0000000..f48e2cb
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/menu/ChecksMenu.java
@@ -0,0 +1,2098 @@
+package me.liwk.karhu.menu;
+
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.check.api.Category;
+import me.liwk.karhu.gui.Button;
+import me.liwk.karhu.gui.Gui;
+import me.liwk.karhu.gui.ItemUtil;
+import me.liwk.karhu.util.check.EnabledUtil;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+
+import java.util.Arrays;
+
+public class ChecksMenu {
+
+
+ private static String getBooleanValue(boolean value) {
+ return value ? "§aEnabled" : "§cDisabled";
+ }
+
+
+ public static void openCheckSelectorMenu(Player opener) {
+ int size = 11;
+ Gui gui = new Gui(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getGuiName()), 27);
+ for (Category category : Category.values()) {
+ gui.addButton(new Button(1, size, ItemUtil.makeItem(Material.BOOK, 1, "§b§l" + category.name(), Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Handle §b" + category.name() + " §7checks",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ ChecksMenu.openChecksMenu(clicker, category.name());
+ }
+ });
+
+ size += 2;
+
+ }
+ gui.addButton(new Button(1, 26, ItemUtil.makeItem(Material.ARROW, 1, "§b§lReturn", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Return to main page",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ MainMenu.openMenu(clicker);
+ }
+ });
+ gui.open(opener);
+ opener.updateInventory();
+ }
+
+ public static void openChecksMenu(Player opener, String type) {
+ Gui gui = new Gui(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getGuiName()), 9 * 5);
+ if(type.equals("COMBAT")) {
+ gui.addButton(new Button(1, 0, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("ReachA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lReach A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("ReachA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("ReachA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if(clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("ReachA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ReachA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ReachA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("ReachA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ReachA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ReachA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 1, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("ReachB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lReach B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("ReachB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("ReachB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT)
+ if (EnabledUtil.checkIfIsEnabled("ReachB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ReachB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ReachB.enabled", true);
+ opener.updateInventory();
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("ReachB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ReachB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ReachB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+
+ gui.addButton(new Button(1, 2, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AutoclickerA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAutoclicker A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AutoclickerA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AutoclickerA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT)
+ if (EnabledUtil.checkIfIsEnabled("AutoclickerA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerA.enabled", true);
+ opener.updateInventory();
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AutoclickerA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 3, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AutoclickerB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAutoclicker B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AutoclickerB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AutoclickerB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AutoclickerB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AutoclickerB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 4, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AutoclickerC")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAutoclicker C", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AutoclickerC")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AutoclickerC")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AutoclickerC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerC.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerC.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AutoclickerC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerC.autoban", false);
+
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerC.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 5, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AutoclickerD")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAutoclicker D", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AutoclickerD")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AutoclickerD")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AutoclickerD")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerD.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerD.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AutoclickerD")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerD.autoban", false);
+
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerD.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 6, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AutoclickerE")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAutoclicker E", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AutoclickerE")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AutoclickerE")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AutoclickerE")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerE.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerE.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AutoclickerE")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerE.autoban", false);
+
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerE.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 7, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AutoclickerF")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAutoclicker F", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AutoclickerF")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AutoclickerF")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AutoclickerF")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerF.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerF.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AutoclickerF")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerF.autoban", false);
+
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AutoclickerF.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+
+ gui.addButton(new Button(1, 8, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraA.autoban", false);
+
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 9, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 10, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraC")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura C", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraC")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraC")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraC.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraC.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraC.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraC.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 11, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraD")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura D", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraD")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraD")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraD")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraD.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraD.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraD")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraD.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraD.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 12, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraE")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura E", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraE")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraE")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraE")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraE.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraE.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraE")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraE.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraE.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 13, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraF")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura F", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraF")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraF")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraF")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraF.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraF.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraF")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraF.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraF.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 14, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraG")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura G", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraG")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraG")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraG")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraG.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraG.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraG")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraG.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraG.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 15, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraH")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura H", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraH")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraH")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraH")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraH.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraH.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraH")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraH.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraH.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 16, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraI")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura I", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraI")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraI")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraI")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraI.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraI.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraI")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraI.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraI.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 17, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("KillauraJ")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lKillaura J", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("KillauraJ")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("KillauraJ")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("KillauraJ")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraJ.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraJ.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("KillauraJ")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraJ.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.KillauraJ.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+
+ gui.addButton(new Button(1, 18, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 19, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 20, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimC")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim C", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimC")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimC")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimC.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimC.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimC.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimC.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 21, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimD")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim D", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimD")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimD")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimD")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimD.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimD.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimD")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimD.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimD.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 22, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimE")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim E", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimE")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimE")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimE")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimE.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimE.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimE")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimE.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimE.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 23, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimF")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim F", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimF")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimF")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimF")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimF.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimF.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimF")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimF.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimF.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 24, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimG")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim G", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimG")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimG")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimG")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimG.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimG.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimG")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimG.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimG.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 25, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimH")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim H", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimH")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimH")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimH")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimH.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimH.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimH")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimH.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimH.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 26, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimI")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim I", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimI")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimI")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimI")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimI.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimI.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimI")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimI.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimI.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 27, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimJ")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim J", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimJ")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimJ")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimJ")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimJ.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimJ.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimJ")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimJ.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimJ.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 28, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimK")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim K", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimK")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimK")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimK")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimK.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimK.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimK")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimK.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimK.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 29, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimL")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim L", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimL")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimL")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimL")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimL.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimL.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimL")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimL.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimL.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 30, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimM")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim M", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimM")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimM")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimM")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimM.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimM.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimM")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimM.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimM.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 31, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("AimO")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lAim O", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("AimO")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("AimO")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("AimO")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimO.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimO.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("AimO")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimO.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.AimO.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 32, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("HitboxA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lHitbox A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("HitboxA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("HitboxA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("HitboxA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.HitboxA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.HitboxA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("HitboxA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.HitboxA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.HitboxA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 33, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("VelocityA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lVelocity A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("VelocityA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("VelocityA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("VelocityA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.VelocityA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.VelocityA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("VelocityA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.VelocityA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.VelocityA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 34, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("VelocityB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lVelocity B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("VelocityB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("VelocityB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("VelocityB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.VelocityB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.VelocityB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("VelocityB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.VelocityB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.VelocityB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "COMBAT");
+ }
+ });
+
+ gui.addButton(new Button(1, 44, ItemUtil.makeItem(Material.ARROW, 1, "§b§lReturn", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Return to selector page",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ openCheckSelectorMenu(clicker);
+ }
+ });
+ }
+ if(type.equals("MOVEMENT")) {
+ gui.addButton(new Button(1, 0, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("SpeedA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lSpeed A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("SpeedA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("SpeedA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("SpeedA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("SpeedA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 1, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("SpeedB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lSpeed B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("SpeedB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("SpeedB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("SpeedB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("SpeedB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 2, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("SpeedC")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lSpeed C", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("SpeedC")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("SpeedC")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("SpeedC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedC.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedC.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("SpeedC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedC.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.SpeedC.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+
+ gui.addButton(new Button(1, 3, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("OmniSprintA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lOmniSprint A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("OmniSprintA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("OmniSprintA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("OmniSprintA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.OmniSprintA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.OmniSprintA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("OmniSprintA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.OmniSprintA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.OmniSprintA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 4, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("MotionA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lMotion A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("MotionA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("MotionA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("MotionA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("MotionA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 5, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("MotionB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lMotion B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("MotionB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("MotionB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("MotionB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("MotionB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 6, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("MotionC")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lMotion C", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("MotionC")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("MotionC")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("MotionC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionC.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionC.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("MotionC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionC.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.MotionC.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 7, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("FlyA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lFly A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("FlyA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("FlyA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("FlyA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FlyA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FlyA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("FlyA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FlyA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FlyA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 8, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("FlyB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lFly B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("FlyB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("FlyB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("FlyB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FlyB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FlyB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("FlyB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FlyB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FlyB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 9, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("StepA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lStep A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("StepA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("StepA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("StepA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.StepA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.StepA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("StepA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.StepA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.StepA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 10, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("StepB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lStep B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("StepB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("StepB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("StepB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.StepB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.StepB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("StepB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.StepB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.StepB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 11, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("NofallA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lNofall A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("NofallA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("NofallA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+
+ gui.close(clicker);
+
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("NofallA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NofallA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NofallA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("NofallA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NofallA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NofallA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 12, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("NofallB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lNofall B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("NofallB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("NofallB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("NofallB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NofallB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NofallB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("NofallB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NofallB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NofallB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 13, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("FastLadderA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lFastLadder A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("FastLadderA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("FastLadderA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("FastLadderA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FastLadderA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FastLadderA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("FastLadderA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FastLadderA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.FastLadderA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 14, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("NoSlowA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lNoSlow A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("NoSlowA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("NoSlowA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("NoSlowA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NoSlowA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NoSlowA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("NoSlowA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NoSlowA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NoSlowA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 15, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("NoSlowB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lNoSlow B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("NoSlowB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("NoSlowB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("NoSlowB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NoSlowB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NoSlowB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("NoSlowB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NoSlowB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.NoSlowB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 16, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("JesusA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lJesus A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("JesusA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("JesusA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("JesusA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.JesusA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.JesusA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("JesusA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.JesusA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.JesusA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 17, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("PhaseA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lPhase A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("PhaseA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("PhaseA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("PhaseA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PhaseA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PhaseA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("PhaseA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PhaseA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PhaseA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 18, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("BoatFlyA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lBoatFly A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("BoatFlyA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("BoatFlyA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("BoatFlyA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BoatFlyA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BoatFlyA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("BoatFlyA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BoatFlyA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BoatFlyA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 19, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("InventoryA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lInventory A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("InventoryA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("InventoryA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("InventoryA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.InventoryA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.InventoryA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("InventoryA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.InventoryA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.InventoryA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 20, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("InventoryB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lInventory B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("InventoryB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("InventoryB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("InventoryB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.InventoryB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.InventoryB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("InventoryB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.InventoryB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.InventoryB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+ gui.addButton(new Button(1, 21, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("ScaffoldA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lScaffold A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("ScaffoldA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("ScaffoldA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("ScaffoldA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ScaffoldA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ScaffoldA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("ScaffoldA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ScaffoldA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.ScaffoldA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "MOVEMENT");
+ }
+ });
+
+
+ gui.addButton(new Button(1, 35, ItemUtil.makeItem(Material.ARROW, 1, "§b§lReturn", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Return to selector page",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ openCheckSelectorMenu(clicker);
+ }
+ });
+ }
+ if(type.equals("PACKET")) {
+ gui.addButton(new Button(1, 0, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("BadPacketsA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lBadPackets A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("BadPacketsA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("BadPacketsA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("BadPacketsA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("BadPacketsA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "PACKET");
+ }
+ });
+
+ gui.addButton(new Button(1, 1, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("BadPacketsB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lBadPackets B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("BadPacketsB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("BadPacketsB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("BadPacketsB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("BadPacketsB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "PACKET");
+ }
+ });
+
+ gui.addButton(new Button(1, 2, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("BadPacketsC")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lBadPackets C", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("BadPacketsC")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("BadPacketsC")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("BadPacketsC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsC.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsC.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("BadPacketsC")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsC.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsC.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "PACKET");
+ }
+ });
+
+ gui.addButton(new Button(1, 3, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("BadPacketsD")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lBadPackets D", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("BadPacketsD")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("BadPacketsD")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("BadPacketsD")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsD.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsD.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("BadPacketsD")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsD.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsD.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "PACKET");
+ }
+ });
+
+ gui.addButton(new Button(1, 4, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("BadPacketsE")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lBadPackets E", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("BadPacketsE")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("BadPacketsE")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("BadPacketsE")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsE.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsE.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("BadPacketsE")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsE.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.BadPacketsE.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "PACKET");
+ }
+ });
+
+ gui.addButton(new Button(1, 5, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("TimerA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lTimer A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("TimerA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("TimerA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("TimerA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.TimerA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.TimerA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("TimerA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.TimerA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.TimerA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "PACKET");
+ }
+ });
+
+ gui.addButton(new Button(1, 6, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("TimerB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lTimer B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("TimerB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("TimerB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("TimerB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.TimerB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.TimerB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("TimerB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.TimerB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.TimerB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "PACKET");
+ }
+ });
+
+ gui.addButton(new Button(1, 7, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("PingSpoofA")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lPingSpoof A", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("PingSpoofA")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("PingSpoofA")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("PingSpoofA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PingSpoofA.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PingSpoofA.enabled", true);
+ opener.updateInventory();
+ }
+ } else if(clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("PingSpoofA")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PingSpoofA.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PingSpoofA.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "PACKET");
+ }
+ });
+
+
+ gui.addButton(new Button(1, 8, ItemUtil.makeItem((EnabledUtil.checkIfIsEnabled("PingSpoofB")) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§b§lPingSpoof B", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7State: §b" + getBooleanValue(EnabledUtil.checkIfIsEnabled("PingSpoofB")),
+ "§7Autoban: §b" + getBooleanValue(EnabledUtil.checkIfIsAutoban("PingSpoofB")),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (clickType == ClickType.LEFT) {
+ if (EnabledUtil.checkIfIsEnabled("PingSpoofB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PingSpoofB.enabled", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PingSpoofB.enabled", true);
+ opener.updateInventory();
+ }
+ } else if (clickType == ClickType.RIGHT) {
+ if (EnabledUtil.checkIfIsAutoban("PingSpoofB")) {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PingSpoofB.autoban", false);
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("checks.PingSpoofB.autoban", true);
+ opener.updateInventory();
+ }
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openChecksMenu(clicker, "PACKET");
+ }
+ });
+
+ gui.addButton(new Button(1, 35, ItemUtil.makeItem(Material.ARROW, 1, "§b§lReturn", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Return to selector page",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ openCheckSelectorMenu(clicker);
+ }
+ });
+ }
+
+ gui.open(opener);
+ opener.updateInventory();
+ }
+ private String getBooleanValue89(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+}
+
diff --git a/src/main/java/me/liwk/karhu/menu/MainMenu.java b/src/main/java/me/liwk/karhu/menu/MainMenu.java
new file mode 100644
index 0000000..8290b71
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/menu/MainMenu.java
@@ -0,0 +1,65 @@
+package me.liwk.karhu.menu;
+
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.check.api.manager.CheckManager;
+import me.liwk.karhu.gui.Button;
+import me.liwk.karhu.gui.Gui;
+import me.liwk.karhu.gui.ItemUtil;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+
+import java.util.Arrays;
+
+public class MainMenu {
+ public static void openMenu(Player opener) {
+
+ Gui gui = new Gui(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getGuiName()), 27);
+ gui.addButton(new Button(1, 11, ItemUtil.makeItem(Material.PAPER, 1, "§b§lChecks", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Manage check states",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ ChecksMenu.openCheckSelectorMenu(clicker);
+ }
+ });
+
+ gui.addButton(new Button(1, 13, ItemUtil.makeItem(Material.NETHER_STAR, 1, "§b§lInfo", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Version: §a" + Karhu.getInstance().getBuild(),
+ "§7TPS: §a" + (int) Karhu.getInstance().getTPS(),
+ "§7RAM: §a" + (int) Karhu.getFreeMemory() + "§7/§a" + Karhu.getMaxMemory(),
+ "§7",
+ "§7Karhu has §b" + new CheckManager(Karhu.getInstance().getPlayerDataManager().getPlayerData(opener)).checkAmount() + " §7different checks",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ opener.updateInventory();
+ }
+ });
+
+ gui.addButton(new Button(1, 15, ItemUtil.makeItem(Material.BOOK, 1, "§b§lSettings", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Control settings",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ SettingsMenu.openSettingsMenu(clicker);
+ }
+ });
+ gui.open(opener);
+ opener.updateInventory();
+ }
+
+ private static String getBooleanValue3(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+}
diff --git a/src/main/java/me/liwk/karhu/menu/SettingsMenu.java b/src/main/java/me/liwk/karhu/menu/SettingsMenu.java
new file mode 100644
index 0000000..c722c30
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/menu/SettingsMenu.java
@@ -0,0 +1,100 @@
+package me.liwk.karhu.menu;
+
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.gui.Button;
+import me.liwk.karhu.gui.Gui;
+import me.liwk.karhu.gui.ItemUtil;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+
+import java.util.Arrays;
+
+public class SettingsMenu {
+
+
+ private static String getBooleanValue(boolean value) {
+ return value ? "§aOn" : "§cOff";
+ }
+
+ private String getBooleanValue2(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "§cOff";
+ }
+
+ public static boolean getAutoban() {
+ return Karhu.getInstance().getFileManager().isAutoban();
+ }
+ public static boolean getPullback() {
+ return Karhu.getInstance().getFileManager().isPullback();
+ }
+
+ public static void openSettingsMenu(Player opener) {
+
+ final boolean pullback = getPullback();
+ final boolean autoban = getAutoban();
+
+ Gui gui = new Gui("Settings", 27);
+
+ gui.addButton(new Button(1, 12, ItemUtil.makeItem((autoban) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§bAutoban", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Autoban: " + getBooleanValue(autoban),
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (autoban) {
+ Karhu.getInstance().getFileManager().getSettings().set("autoban", false);
+ clicker.sendMessage("§b§lKARHU §7// §cAutoban off!");
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("autoban", true);
+ clicker.sendMessage("§b§lKARHU §7// §aAutoban on!");
+ opener.updateInventory();
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openSettingsMenu(clicker);
+ }
+ });
+
+ gui.addButton(new Button(1, 14, ItemUtil.makeItem((pullback) ? Material.EMERALD_BLOCK : Material.REDSTONE_BLOCK, 1, "§bPullback", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Pullback: " + getBooleanValue(pullback),
+ "§7This ensures players can't use movement modules",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ if (pullback) {
+ Karhu.getInstance().getFileManager().getSettings().set("pullback", false);
+ clicker.sendMessage("§b§lKARHU §7// §cPullback off!");
+ } else {
+ Karhu.getInstance().getFileManager().getSettings().set("pullback", true);
+ clicker.sendMessage("§b§lKARHU §7// §aPullback on!");
+ opener.updateInventory();
+ }
+ Karhu.getInstance().getFileManager().save();
+ Karhu.getInstance().getFileManager().load(Karhu.getInstance());
+ openSettingsMenu(clicker);
+ }
+ });
+
+ gui.addButton(new Button(1, 26, ItemUtil.makeItem(Material.ARROW, 1, "§b§lReturn", Arrays.asList(
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤",
+ "§7Return to main page",
+ "§7§m⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤"
+ ))) {
+ @Override
+ public void onClick(Player clicker, ClickType clickType) {
+ gui.close(clicker);
+ MainMenu.openMenu(clicker);
+ }
+ });
+ gui.open(opener);
+ opener.updateInventory();
+ }
+ private String getBooleanValue3(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/playerhandler/Handler1_13.java b/src/main/java/me/liwk/karhu/playerhandler/Handler1_13.java
new file mode 100644
index 0000000..a82aeef
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/playerhandler/Handler1_13.java
@@ -0,0 +1,49 @@
+package me.liwk.karhu.playerhandler;
+
+import io.github.retrooper.packetevents.PacketEvents;
+import io.github.retrooper.packetevents.utils.server.ServerVersion;
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.util.MovementUtils;
+import org.bukkit.entity.Player;
+import org.bukkit.potion.PotionEffectType;
+
+public class Handler1_13 {
+
+ public static boolean isGliding(Player player) {
+ if(PacketEvents.getAPI().getServerUtils().getVersion().isLowerThan(ServerVersion.v_1_9)) {
+ return false;
+ }
+ if(player.isGliding()) Karhu.getInstance().getPlayerDataManager().getPlayerData(player).setLastGlide(System.currentTimeMillis());
+ if(player.isGliding()) Karhu.getInstance().getPlayerDataManager().getPlayerData(player).setGliding(player.isGliding());
+ return player.isGliding();
+ }
+
+ public static boolean isRiptiding(Player player) {
+ if(PacketEvents.getAPI().getServerUtils().getVersion().isLowerThan(ServerVersion.v_1_13)) {
+ return false;
+ }
+ if(player.isRiptiding()) Karhu.getInstance().getPlayerDataManager().getPlayerData(player).setLastRiptide(System.currentTimeMillis());
+ if(player.isRiptiding()) Karhu.getInstance().getPlayerDataManager().getPlayerData(player).setRiptiding(player.isRiptiding());
+ return player.isRiptiding();
+ }
+
+ public static float getDolphinLevel(Player player) {
+ if(PacketEvents.getAPI().getServerUtils().getVersion().isLowerThan(ServerVersion.v_1_12_2)) {
+ return 0;
+ }
+ return (float) MovementUtils.getPotionEffectLevel(player, PotionEffectType.DOLPHINS_GRACE);
+ }
+ public static float getSoulSpeedEnchant(Player player) {
+ if(PacketEvents.getAPI().getServerUtils().getVersion().isLowerThan(ServerVersion.v_1_16)) {
+ return 0;
+ }
+ return MovementUtils.getSoulSpeedLevel(player);
+ }
+
+ public static float getRiptideEnchant(Player player) {
+ if(PacketEvents.getAPI().getServerUtils().getVersion().isLowerThan(ServerVersion.v_1_13)) {
+ return 0;
+ }
+ return MovementUtils.getSoulSpeedLevel(player);
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/playerhandler/Handler1_8.java b/src/main/java/me/liwk/karhu/playerhandler/Handler1_8.java
new file mode 100644
index 0000000..99c15f1
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/playerhandler/Handler1_8.java
@@ -0,0 +1,19 @@
+package me.liwk.karhu.playerhandler;
+
+import io.github.retrooper.packetevents.PacketEvents;import io.github.retrooper.packetevents.utils.server.ServerVersion;
+import org.bukkit.GameMode;
+import org.bukkit.entity.Player;
+
+public class Handler1_8 {
+
+ public static boolean isGliding(Player player) {
+ return false;
+ }
+
+ public static boolean isSpectating(Player player) {
+ if(PacketEvents.getAPI().getServerUtils().getVersion().isLowerThan(ServerVersion.v_1_8)) {
+ return false;
+ }
+ return player.getGameMode().equals(GameMode.SPECTATOR);
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/playerhandler/Handler1_9.java b/src/main/java/me/liwk/karhu/playerhandler/Handler1_9.java
new file mode 100644
index 0000000..0fea8a3
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/playerhandler/Handler1_9.java
@@ -0,0 +1,23 @@
+package me.liwk.karhu.playerhandler;
+
+import io.github.retrooper.packetevents.PacketEvents;
+import io.github.retrooper.packetevents.utils.server.ServerVersion;
+import me.liwk.karhu.Karhu;
+import org.bukkit.entity.Player;
+
+public class Handler1_9 {
+
+ public static boolean isGliding(Player player) {
+ if(PacketEvents.getAPI().getServerUtils().getVersion().isLowerThan(ServerVersion.v_1_9)) {
+ return false;
+ }
+
+ if(PacketEvents.getAPI().getServerUtils().getVersion().equals(ServerVersion.ERROR)) {
+ return false;
+ }
+
+ if(player.isGliding()) Karhu.getInstance().getPlayerDataManager().getPlayerData(player).setLastGlide(System.currentTimeMillis());
+ if(player.isGliding()) Karhu.getInstance().getPlayerDataManager().getPlayerData(player).setGliding(player.isGliding());
+ return player.isGliding();
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/playerhandler/VersionBridgeHelper.java b/src/main/java/me/liwk/karhu/playerhandler/VersionBridgeHelper.java
new file mode 100644
index 0000000..6263f49
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/playerhandler/VersionBridgeHelper.java
@@ -0,0 +1,19 @@
+package me.liwk.karhu.playerhandler;
+
+import io.github.retrooper.packetevents.utils.server.ServerVersion;
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.data.PlayerData;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+public final class VersionBridgeHelper {
+
+ public static ItemStack getStackInHand(PlayerData data){
+ return getStackInHand(data.getDataPlayer());
+ }
+
+ public static ItemStack getStackInHand(Player data){
+ return Karhu.SERVER_VERSION.isHigherThan(ServerVersion.v_1_8_8) ? data.getInventory().getItemInMainHand() : data.getItemInHand();
+ }
+
+}
diff --git a/src/main/java/me/liwk/karhu/util/AxisAlignedBB.java b/src/main/java/me/liwk/karhu/util/AxisAlignedBB.java
new file mode 100644
index 0000000..9c63628
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/AxisAlignedBB.java
@@ -0,0 +1,415 @@
+package me.liwk.karhu.util;
+
+public class AxisAlignedBB
+{
+ public double minX;
+ public double minY;
+ public double minZ;
+ public double maxX;
+ public double maxY;
+ public double maxZ;
+
+ public AxisAlignedBB(double x1, double y1, double z1, double x2, double y2, double z2)
+ {
+ this.minX = Math.min(x1, x2);
+ this.minY = Math.min(y1, y2);
+ this.minZ = Math.min(z1, z2);
+ this.maxX = Math.max(x1, x2);
+ this.maxY = Math.max(y1, y2);
+ this.maxZ = Math.max(z1, z2);
+ }
+
+ public AxisAlignedBB(BlockPos pos1, BlockPos pos2) {
+ this.minX = (double)pos1.getX();
+ this.minY = (double)pos1.getY();
+ this.minZ = (double)pos1.getZ();
+ this.maxX = (double)pos2.getX();
+ this.maxY = (double)pos2.getY();
+ this.maxZ = (double)pos2.getZ();
+ }
+
+ private String nej(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+ /**
+ * Adds the coordinates to the bounding box extending it if the point lies outside the current ranges. Args: x, y, z
+ */
+ public AxisAlignedBB addCoord(double x, double y, double z)
+ {
+ double d0 = this.minX;
+ double d1 = this.minY;
+ double d2 = this.minZ;
+ double d3 = this.maxX;
+ double d4 = this.maxY;
+ double d5 = this.maxZ;
+
+ if (x < 0.0D)
+ {
+ d0 += x;
+ }
+ else if (x > 0.0D)
+ {
+ d3 += x;
+ }
+
+ if (y < 0.0D)
+ {
+ d1 += y;
+ }
+ else if (y > 0.0D)
+ {
+ d4 += y;
+ }
+
+ if (z < 0.0D)
+ {
+ d2 += z;
+ }
+ else if (z > 0.0D)
+ {
+ d5 += z;
+ }
+
+ return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
+ }
+
+ /**
+ * Returns a bounding box expanded by the specified vector (if negative numbers are given it will shrink). Args: x,
+ * y, z
+ */
+ public AxisAlignedBB expand(double x, double y, double z)
+ {
+ double d0 = this.minX - x;
+ double d1 = this.minY - y;
+ double d2 = this.minZ - z;
+ double d3 = this.maxX + x;
+ double d4 = this.maxY + y;
+ double d5 = this.maxZ + z;
+ return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
+ }
+
+ public AxisAlignedBB union(AxisAlignedBB other)
+ {
+ double d0 = Math.min(this.minX, other.minX);
+ double d1 = Math.min(this.minY, other.minY);
+ double d2 = Math.min(this.minZ, other.minZ);
+ double d3 = Math.max(this.maxX, other.maxX);
+ double d4 = Math.max(this.maxY, other.maxY);
+ double d5 = Math.max(this.maxZ, other.maxZ);
+ return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
+ }
+
+ /**
+ * returns an AABB with corners x1, y1, z1 and x2, y2, z2
+ */
+ public static AxisAlignedBB fromBounds(double x1, double y1, double z1, double x2, double y2, double z2)
+ {
+ double d0 = Math.min(x1, x2);
+ double d1 = Math.min(y1, y2);
+ double d2 = Math.min(z1, z2);
+ double d3 = Math.max(x1, x2);
+ double d4 = Math.max(y1, y2);
+ double d5 = Math.max(z1, z2);
+ return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
+ }
+
+ /**
+ * Offsets the current bounding box by the specified coordinates. Args: x, y, z
+ */
+ public AxisAlignedBB offset(double x, double y, double z)
+ {
+ return new AxisAlignedBB(this.minX + x, this.minY + y, this.minZ + z, this.maxX + x, this.maxY + y, this.maxZ + z);
+ }
+
+ /**
+ * if instance and the argument bounding boxes overlap in the Y and Z dimensions, calculate the offset between them
+ * in the X dimension. return var2 if the bounding boxes do not overlap or if var2 is closer to 0 then the
+ * calculated offset. Otherwise return the calculated offset.
+ */
+ public double calculateXOffset(AxisAlignedBB other, double offsetX)
+ {
+ if (other.maxY > this.minY && other.minY < this.maxY && other.maxZ > this.minZ && other.minZ < this.maxZ)
+ {
+ if (offsetX > 0.0D && other.maxX <= this.minX)
+ {
+ double d1 = this.minX - other.maxX;
+
+ if (d1 < offsetX)
+ {
+ offsetX = d1;
+ }
+ }
+ else if (offsetX < 0.0D && other.minX >= this.maxX)
+ {
+ double d0 = this.maxX - other.minX;
+
+ if (d0 > offsetX)
+ {
+ offsetX = d0;
+ }
+ }
+
+ return offsetX;
+ }
+ else
+ {
+ return offsetX;
+ }
+ }
+
+ /**
+ * if instance and the argument bounding boxes overlap in the X and Z dimensions, calculate the offset between them
+ * in the Y dimension. return var2 if the bounding boxes do not overlap or if var2 is closer to 0 then the
+ * calculated offset. Otherwise return the calculated offset.
+ */
+ public double calculateYOffset(AxisAlignedBB other, double offsetY)
+ {
+ if (other.maxX > this.minX && other.minX < this.maxX && other.maxZ > this.minZ && other.minZ < this.maxZ)
+ {
+ if (offsetY > 0.0D && other.maxY <= this.minY)
+ {
+ double d1 = this.minY - other.maxY;
+
+ if (d1 < offsetY)
+ {
+ offsetY = d1;
+ }
+ }
+ else if (offsetY < 0.0D && other.minY >= this.maxY)
+ {
+ double d0 = this.maxY - other.minY;
+
+ if (d0 > offsetY)
+ {
+ offsetY = d0;
+ }
+ }
+
+ return offsetY;
+ }
+ else
+ {
+ return offsetY;
+ }
+ }
+
+ /**
+ * if instance and the argument bounding boxes overlap in the Y and X dimensions, calculate the offset between them
+ * in the Z dimension. return var2 if the bounding boxes do not overlap or if var2 is closer to 0 then the
+ * calculated offset. Otherwise return the calculated offset.
+ */
+ public double calculateZOffset(AxisAlignedBB other, double offsetZ)
+ {
+ if (other.maxX > this.minX && other.minX < this.maxX && other.maxY > this.minY && other.minY < this.maxY)
+ {
+ if (offsetZ > 0.0D && other.maxZ <= this.minZ)
+ {
+ double d1 = this.minZ - other.maxZ;
+
+ if (d1 < offsetZ)
+ {
+ offsetZ = d1;
+ }
+ }
+ else if (offsetZ < 0.0D && other.minZ >= this.maxZ)
+ {
+ double d0 = this.maxZ - other.minZ;
+
+ if (d0 > offsetZ)
+ {
+ offsetZ = d0;
+ }
+ }
+
+ return offsetZ;
+ }
+ else
+ {
+ return offsetZ;
+ }
+ }
+
+ /**
+ * Returns whether the given bounding box intersects with this one. Args: axisAlignedBB
+ */
+ public boolean intersectsWith(AxisAlignedBB other)
+ {
+ return other.maxX > this.minX && other.minX < this.maxX ? (other.maxY > this.minY && other.minY < this.maxY ? other.maxZ > this.minZ && other.minZ < this.maxZ : false) : false;
+ }
+
+ /**
+ * Returns if the supplied Vec3D is completely inside the bounding box
+ */
+ public boolean isVecInside(Vec3 vec)
+ {
+ return vec.xCoord > this.minX && vec.xCoord < this.maxX ? (vec.yCoord > this.minY && vec.yCoord < this.maxY ? vec.zCoord > this.minZ && vec.zCoord < this.maxZ : false) : false;
+ }
+
+ /**
+ * Returns the average length of the edges of the bounding box.
+ */
+ public double getAverageEdgeLength()
+ {
+ double d0 = this.maxX - this.minX;
+ double d1 = this.maxY - this.minY;
+ double d2 = this.maxZ - this.minZ;
+ return (d0 + d1 + d2) / 3.0D;
+ }
+
+ /**
+ * Returns a bounding box that is inset by the specified amounts
+ */
+ public AxisAlignedBB contract(double x, double y, double z)
+ {
+ double d0 = this.minX + x;
+ double d1 = this.minY + y;
+ double d2 = this.minZ + z;
+ double d3 = this.maxX - x;
+ double d4 = this.maxY - y;
+ double d5 = this.maxZ - z;
+ return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
+ }
+
+ public MovingObjectPosition calculateIntercept(Vec3 vecA, Vec3 vecB)
+ {
+ Vec3 vec3 = vecA.getIntermediateWithXValue(vecB, this.minX);
+ Vec3 vec31 = vecA.getIntermediateWithXValue(vecB, this.maxX);
+ Vec3 vec32 = vecA.getIntermediateWithYValue(vecB, this.minY);
+ Vec3 vec33 = vecA.getIntermediateWithYValue(vecB, this.maxY);
+ Vec3 vec34 = vecA.getIntermediateWithZValue(vecB, this.minZ);
+ Vec3 vec35 = vecA.getIntermediateWithZValue(vecB, this.maxZ);
+
+ if (!this.isVecInYZ(vec3))
+ {
+ vec3 = null;
+ }
+
+ if (!this.isVecInYZ(vec31))
+ {
+ vec31 = null;
+ }
+
+ if (!this.isVecInXZ(vec32))
+ {
+ vec32 = null;
+ }
+
+ if (!this.isVecInXZ(vec33))
+ {
+ vec33 = null;
+ }
+
+ if (!this.isVecInXY(vec34))
+ {
+ vec34 = null;
+ }
+
+ if (!this.isVecInXY(vec35))
+ {
+ vec35 = null;
+ }
+
+ Vec3 vec36 = null;
+
+ if (vec3 != null)
+ {
+ vec36 = vec3;
+ }
+
+ if (vec31 != null && (vec36 == null || vecA.squareDistanceTo(vec31) < vecA.squareDistanceTo(vec36)))
+ {
+ vec36 = vec31;
+ }
+
+ if (vec32 != null && (vec36 == null || vecA.squareDistanceTo(vec32) < vecA.squareDistanceTo(vec36)))
+ {
+ vec36 = vec32;
+ }
+
+ if (vec33 != null && (vec36 == null || vecA.squareDistanceTo(vec33) < vecA.squareDistanceTo(vec36)))
+ {
+ vec36 = vec33;
+ }
+
+ if (vec34 != null && (vec36 == null || vecA.squareDistanceTo(vec34) < vecA.squareDistanceTo(vec36)))
+ {
+ vec36 = vec34;
+ }
+
+ if (vec35 != null && (vec36 == null || vecA.squareDistanceTo(vec35) < vecA.squareDistanceTo(vec36)))
+ {
+ vec36 = vec35;
+ }
+
+ if (vec36 == null)
+ {
+ return null;
+ }
+ else
+ {
+ EnumFacing enumfacing = null;
+
+ if (vec36 == vec3)
+ {
+ enumfacing = EnumFacing.WEST;
+ }
+ else if (vec36 == vec31)
+ {
+ enumfacing = EnumFacing.EAST;
+ }
+ else if (vec36 == vec32)
+ {
+ enumfacing = EnumFacing.DOWN;
+ }
+ else if (vec36 == vec33)
+ {
+ enumfacing = EnumFacing.UP;
+ }
+ else if (vec36 == vec34)
+ {
+ enumfacing = EnumFacing.NORTH;
+ }
+ else
+ {
+ enumfacing = EnumFacing.SOUTH;
+ }
+
+ return new MovingObjectPosition(vec36, enumfacing);
+ }
+ }
+
+ /**
+ * Checks if the specified vector is within the YZ dimensions of the bounding box. Args: Vec3D
+ */
+ private boolean isVecInYZ(Vec3 vec)
+ {
+ return vec == null ? false : vec.yCoord >= this.minY && vec.yCoord <= this.maxY && vec.zCoord >= this.minZ && vec.zCoord <= this.maxZ;
+ }
+
+ /**
+ * Checks if the specified vector is within the XZ dimensions of the bounding box. Args: Vec3D
+ */
+ private boolean isVecInXZ(Vec3 vec)
+ {
+ return vec == null ? false : vec.xCoord >= this.minX && vec.xCoord <= this.maxX && vec.zCoord >= this.minZ && vec.zCoord <= this.maxZ;
+ }
+
+ /**
+ * Checks if the specified vector is within the XY dimensions of the bounding box. Args: Vec3D
+ */
+ private boolean isVecInXY(Vec3 vec)
+ {
+ return vec == null ? false : vec.xCoord >= this.minX && vec.xCoord <= this.maxX && vec.yCoord >= this.minY && vec.yCoord <= this.maxY;
+ }
+
+ public String toString()
+ {
+ return "box[" + this.minX + ", " + this.minY + ", " + this.minZ + " -> " + this.maxX + ", " + this.maxY + ", " + this.maxZ + "]";
+ }
+
+ public boolean func_181656_b()
+ {
+ return Double.isNaN(this.minX) || Double.isNaN(this.minY) || Double.isNaN(this.minZ) || Double.isNaN(this.maxX) || Double.isNaN(this.maxY) || Double.isNaN(this.maxZ);
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/util/BlockPos.java b/src/main/java/me/liwk/karhu/util/BlockPos.java
new file mode 100644
index 0000000..b25db00
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/BlockPos.java
@@ -0,0 +1,361 @@
+package me.liwk.karhu.util;
+
+import com.google.common.collect.AbstractIterator;
+
+import java.util.Iterator;
+
+public class BlockPos extends Vec3i
+{
+ /** The BlockPos with all coordinates 0 */
+ public static final BlockPos ORIGIN = new BlockPos(0, 0, 0);
+ private static final int NUM_X_BITS = 1 + MathHelper.calculateLogBaseTwo(MathHelper.roundUpToPowerOfTwo(30000000));
+ private static final int NUM_Z_BITS = NUM_X_BITS;
+ private static final int NUM_Y_BITS = 64 - NUM_X_BITS - NUM_Z_BITS;
+ private static final int Y_SHIFT = 0 + NUM_Z_BITS;
+ private static final int X_SHIFT = Y_SHIFT + NUM_Y_BITS;
+ private static final long X_MASK = (1L << NUM_X_BITS) - 1L;
+ private static final long Y_MASK = (1L << NUM_Y_BITS) - 1L;
+ private static final long Z_MASK = (1L << NUM_Z_BITS) - 1L;
+
+ public BlockPos(int x, int y, int z)
+ {
+ super(x, y, z);
+ }
+
+ public BlockPos(double x, double y, double z)
+ {
+ super(x, y, z);
+ }
+
+ public BlockPos(Vec3 source)
+ {
+ this(source.xCoord, source.yCoord, source.zCoord);
+ }
+
+ public BlockPos(Vec3i source)
+ {
+ this(source.getX(), source.getY(), source.getZ());
+ }
+
+ /**
+ * Add the given coordinates to the coordinates of this BlockPos
+ */
+ public BlockPos add(double x, double y, double z)
+ {
+ return x == 0.0D && y == 0.0D && z == 0.0D ? this : new BlockPos((double)this.getX() + x, (double)this.getY() + y, (double)this.getZ() + z);
+ }
+
+ /**
+ * Add the given coordinates to the coordinates of this BlockPos
+ */
+ public BlockPos add(int x, int y, int z)
+ {
+ return x == 0 && y == 0 && z == 0 ? this : new BlockPos(this.getX() + x, this.getY() + y, this.getZ() + z);
+ }
+
+ /**
+ * Add the given Vector to this BlockPos
+ */
+ public BlockPos add(Vec3i vec)
+ {
+ return vec.getX() == 0 && vec.getY() == 0 && vec.getZ() == 0 ? this : new BlockPos(this.getX() + vec.getX(), this.getY() + vec.getY(), this.getZ() + vec.getZ());
+ }
+
+ /**
+ * Subtract the given Vector from this BlockPos
+ */
+ public BlockPos subtract(Vec3i vec)
+ {
+ return vec.getX() == 0 && vec.getY() == 0 && vec.getZ() == 0 ? this : new BlockPos(this.getX() - vec.getX(), this.getY() - vec.getY(), this.getZ() - vec.getZ());
+ }
+
+ /**
+ * Offset this BlockPos 1 block up
+ */
+ public BlockPos up()
+ {
+ return this.up(1);
+ }
+
+ /**
+ * Offset this BlockPos n blocks up
+ */
+ public BlockPos up(int n)
+ {
+ return this.offset(EnumFacing.UP, n);
+ }
+
+ /**
+ * Offset this BlockPos 1 block down
+ */
+ public BlockPos down()
+ {
+ return this.down(1);
+ }
+
+ /**
+ * Offset this BlockPos n blocks down
+ */
+ public BlockPos down(int n)
+ {
+ return this.offset(EnumFacing.DOWN, n);
+ }
+
+ /**
+ * Offset this BlockPos 1 block in northern direction
+ */
+ public BlockPos north()
+ {
+ return this.north(1);
+ }
+
+ /**
+ * Offset this BlockPos n blocks in northern direction
+ */
+ public BlockPos north(int n)
+ {
+ return this.offset(EnumFacing.NORTH, n);
+ }
+
+ /**
+ * Offset this BlockPos 1 block in southern direction
+ */
+ public BlockPos south()
+ {
+ return this.south(1);
+ }
+
+ /**
+ * Offset this BlockPos n blocks in southern direction
+ */
+ public BlockPos south(int n)
+ {
+ return this.offset(EnumFacing.SOUTH, n);
+ }
+
+ /**
+ * Offset this BlockPos 1 block in western direction
+ */
+ public BlockPos west()
+ {
+ return this.west(1);
+ }
+
+ /**
+ * Offset this BlockPos n blocks in western direction
+ */
+ public BlockPos west(int n)
+ {
+ return this.offset(EnumFacing.WEST, n);
+ }
+
+ /**
+ * Offset this BlockPos 1 block in eastern direction
+ */
+ public BlockPos east()
+ {
+ return this.east(1);
+ }
+
+ /**
+ * Offset this BlockPos n blocks in eastern direction
+ */
+ public BlockPos east(int n)
+ {
+ return this.offset(EnumFacing.EAST, n);
+ }
+
+ /**
+ * Offset this BlockPos 1 block in the given direction
+ */
+ public BlockPos offset(EnumFacing facing)
+ {
+ return this.offset(facing, 1);
+ }
+
+ /**
+ * Offsets this BlockPos n blocks in the given direction
+ */
+ public BlockPos offset(EnumFacing facing, int n)
+ {
+ return n == 0 ? this : new BlockPos(this.getX() + facing.getFrontOffsetX() * n, this.getY() + facing.getFrontOffsetY() * n, this.getZ() + facing.getFrontOffsetZ() * n);
+ }
+
+ /**
+ * Calculate the cross product of this and the given Vector
+ */
+ public BlockPos crossProduct(Vec3i vec)
+ {
+ return new BlockPos(this.getY() * vec.getZ() - this.getZ() * vec.getY(), this.getZ() * vec.getX() - this.getX() * vec.getZ(), this.getX() * vec.getY() - this.getY() * vec.getX());
+ }
+
+ /**
+ * Serialize this BlockPos into a long value
+ */
+ public long toLong()
+ {
+ return ((long)this.getX() & X_MASK) << X_SHIFT | ((long)this.getY() & Y_MASK) << Y_SHIFT | ((long)this.getZ() & Z_MASK) << 0;
+ }
+
+ /**
+ * Create a BlockPos from a serialized long value (created by toLong)
+ */
+ public static BlockPos fromLong(long serialized)
+ {
+ int i = (int)(serialized << 64 - X_SHIFT - NUM_X_BITS >> 64 - NUM_X_BITS);
+ int j = (int)(serialized << 64 - Y_SHIFT - NUM_Y_BITS >> 64 - NUM_Y_BITS);
+ int k = (int)(serialized << 64 - NUM_Z_BITS >> 64 - NUM_Z_BITS);
+ return new BlockPos(i, j, k);
+ }
+
+ public static Iterable getAllInBox(BlockPos from, BlockPos to)
+ {
+ final BlockPos blockpos = new BlockPos(Math.min(from.getX(), to.getX()), Math.min(from.getY(), to.getY()), Math.min(from.getZ(), to.getZ()));
+ final BlockPos blockpos1 = new BlockPos(Math.max(from.getX(), to.getX()), Math.max(from.getY(), to.getY()), Math.max(from.getZ(), to.getZ()));
+ return new Iterable()
+ {
+ public Iterator iterator()
+ {
+ return new AbstractIterator()
+ {
+ private BlockPos lastReturned = null;
+ protected BlockPos computeNext()
+ {
+ if (this.lastReturned == null)
+ {
+ this.lastReturned = blockpos;
+ return this.lastReturned;
+ }
+ else if (this.lastReturned.equals(blockpos1))
+ {
+ return (BlockPos)this.endOfData();
+ }
+ else
+ {
+ int i = this.lastReturned.getX();
+ int j = this.lastReturned.getY();
+ int k = this.lastReturned.getZ();
+
+ if (i < blockpos1.getX())
+ {
+ ++i;
+ }
+ else if (j < blockpos1.getY())
+ {
+ i = blockpos.getX();
+ ++j;
+ }
+ else if (k < blockpos1.getZ())
+ {
+ i = blockpos.getX();
+ j = blockpos.getY();
+ ++k;
+ }
+
+ this.lastReturned = new BlockPos(i, j, k);
+ return this.lastReturned;
+ }
+ }
+ };
+ }
+ };
+ }
+
+ public static Iterable getAllInBoxMutable(BlockPos from, BlockPos to)
+ {
+ final BlockPos blockpos = new BlockPos(Math.min(from.getX(), to.getX()), Math.min(from.getY(), to.getY()), Math.min(from.getZ(), to.getZ()));
+ final BlockPos blockpos1 = new BlockPos(Math.max(from.getX(), to.getX()), Math.max(from.getY(), to.getY()), Math.max(from.getZ(), to.getZ()));
+ return new Iterable()
+ {
+ public Iterator iterator()
+ {
+ return new AbstractIterator()
+ {
+ private BlockPos.MutableBlockPos theBlockPos = null;
+ protected BlockPos.MutableBlockPos computeNext()
+ {
+ if (this.theBlockPos == null)
+ {
+ this.theBlockPos = new BlockPos.MutableBlockPos(blockpos.getX(), blockpos.getY(), blockpos.getZ());
+ return this.theBlockPos;
+ }
+ else if (this.theBlockPos.equals(blockpos1))
+ {
+ return (BlockPos.MutableBlockPos)this.endOfData();
+ }
+ else
+ {
+ int i = this.theBlockPos.getX();
+ int j = this.theBlockPos.getY();
+ int k = this.theBlockPos.getZ();
+
+ if (i < blockpos1.getX())
+ {
+ ++i;
+ }
+ else if (j < blockpos1.getY())
+ {
+ i = blockpos.getX();
+ ++j;
+ }
+ else if (k < blockpos1.getZ())
+ {
+ i = blockpos.getX();
+ j = blockpos.getY();
+ ++k;
+ }
+
+ this.theBlockPos.x = i;
+ this.theBlockPos.y = j;
+ this.theBlockPos.z = k;
+ return this.theBlockPos;
+ }
+ }
+ };
+ }
+ };
+ }
+
+ public static final class MutableBlockPos extends BlockPos
+ {
+ private int x;
+ private int y;
+ private int z;
+
+ public MutableBlockPos()
+ {
+ this(0, 0, 0);
+ }
+
+ public MutableBlockPos(int x_, int y_, int z_)
+ {
+ super(0, 0, 0);
+ this.x = x_;
+ this.y = y_;
+ this.z = z_;
+ }
+
+ public int getX()
+ {
+ return this.x;
+ }
+
+ public int getY()
+ {
+ return this.y;
+ }
+
+ public int getZ()
+ {
+ return this.z;
+ }
+
+ public BlockPos.MutableBlockPos func_181079_c(int p_181079_1_, int p_181079_2_, int p_181079_3_)
+ {
+ this.x = p_181079_1_;
+ this.y = p_181079_2_;
+ this.z = p_181079_3_;
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/util/BoundingBox.java b/src/main/java/me/liwk/karhu/util/BoundingBox.java
new file mode 100644
index 0000000..e4000ec
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/BoundingBox.java
@@ -0,0 +1,270 @@
+package me.liwk.karhu.util;
+
+import lombok.Getter;
+import me.liwk.karhu.data.PlayerData;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.util.Vector;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+@Getter
+public final class BoundingBox {
+
+ private double minX, minY, minZ;
+
+ private double maxX, maxY, maxZ;
+
+ private boolean worldLoaded;
+
+ private long timestamp;
+
+ public BoundingBox(final Location position) {
+ this(position.getX(), position.getY(), position.getZ());
+ }
+
+ public BoundingBox(final double x, final double y, final double z) {
+ this(x, x, y, y, z, z);
+
+ timestamp = System.currentTimeMillis();
+ }
+
+ public BoundingBox(double minX, double maxX, double minY, double maxY, double minZ, double maxZ) {
+ if (minX < maxX) {
+ this.minX = minX;
+ this.maxX = maxX;
+ } else {
+ this.minX = maxX;
+ this.maxX = minX;
+ }
+ if (minY < maxY) {
+ this.minY = minY;
+ this.maxY = maxY;
+ } else {
+ this.minY = maxY;
+ this.maxY = minY;
+ }
+ if (minZ < maxZ) {
+ this.minZ = minZ;
+ this.maxZ = maxZ;
+ } else {
+ this.minZ = maxZ;
+ this.maxZ = minZ;
+ }
+ }
+
+ public double distance(final Location location) {
+ return Math.sqrt(Math.min(Math.pow(location.getX() - this.minX, 2), Math.pow(location.getX() - this.maxX, 2)) + Math.min(Math.pow(location.getZ() - this.minZ, 2), Math.pow(location.getZ() - this.maxZ, 2)));
+ }
+
+ public double distance(final double x, final double z) {
+
+ final double dx = Math.min(Math.pow(x - minX, 2), Math.pow(x - maxX, 2));
+ final double dz = Math.min(Math.pow(z - minZ, 2), Math.pow(z - maxZ, 2));
+
+ return Math.sqrt(dx + dz);
+ }
+
+ public double distance(final BoundingBox box) {
+
+ final double dx = Math.min(Math.pow(box.minX - minX, 2), Math.pow(box.maxX - maxX, 2));
+ final double dz = Math.min(Math.pow(box.minZ - minZ, 2), Math.pow(box.maxZ - maxZ, 2));
+
+ return Math.sqrt(dx + dz);
+ }
+
+ public double angle(final World world, final BoundingBox box) {
+ final Vector homeVector = new Vector(minX, minY, minZ);
+ final Vector outVector = new Vector(box.minX, box.minY, box.minZ);
+
+ return outVector.subtract(homeVector).setY(0).angle(outVector.toLocation(world).getDirection().setY(0));
+ }
+
+ public Vector getDirection(final World world) {
+ return new Location(world, minX, minY, minZ).getDirection();
+ }
+
+ public BoundingBox add(final BoundingBox box) {
+
+ this.minX += box.minX;
+ this.minY += box.minY;
+ this.minZ += box.minZ;
+
+ this.maxX += box.maxX;
+ this.maxY += box.maxY;
+ this.maxZ += box.maxZ;
+
+ return this;
+ }
+
+ public BoundingBox move(final double x, final double y, final double z) {
+
+ this.minX += x;
+ this.minY += y;
+ this.minZ += z;
+
+ this.maxX += x;
+ this.maxY += y;
+ this.maxZ += z;
+
+ return this;
+ }
+
+ public BoundingBox expand(final double x, final double y, final double z) {
+
+ this.minX -= x;
+ this.minY -= y;
+ this.minZ -= z;
+
+ this.maxX += x;
+ this.maxY += y;
+ this.maxZ += z;
+
+ return this;
+ }
+
+ public BoundingBox expandMin(final double x, final double y, final double z) {
+ this.minX += x;
+ this.minY += y;
+ this.minZ += z;
+ return this;
+ }
+
+ public BoundingBox expandMax(double x, double y, double z) {
+ this.maxX += x;
+ this.maxY += y;
+ this.maxZ += z;
+
+ return this;
+ }
+
+
+ public boolean checkBlocks(final World world, final Predicate predicate) {
+ final int first = (int) Math.floor(this.minX);
+ final int second = (int) Math.ceil(this.maxX);
+ final int third = (int) Math.floor(this.minY);
+ final int forth = (int) Math.ceil(this.maxY);
+ final int fifth = (int) Math.floor(this.minZ);
+ final int sixth = (int) Math.ceil(this.maxZ);
+
+ final ArrayList list = new ArrayList<>();
+ worldLoaded = true;
+
+ Location locMin = new Location(world, first, third, fifth);
+ Location locMax = new Location(world, second, forth, sixth);
+
+ if(!locMin.getWorld().isChunkLoaded(locMin.getBlockX() >> 4, locMin.getBlockZ() >> 4) || !locMax.getWorld().isChunkLoaded(locMax.getBlockX() >> 4, locMax.getBlockZ() >> 4)) {
+ worldLoaded = false;
+ }
+
+ if(worldLoaded) list.add(world.getBlockAt(first, third, fifth));
+
+ for (int i = first; i < second; ++i) {
+ for (int j = third; j < forth; ++j) {
+ for (int k = fifth; k < sixth; ++k) {
+
+ Location locShit = new Location(world, i, j, k);
+ if(locShit.getWorld().isChunkLoaded(locShit.getBlockX() >> 4, locShit.getBlockZ() >> 4)) {
+ list.add(world.getBlockAt(i, j, k));
+ } else {
+ worldLoaded = false;
+ }
+ }
+ }
+ }
+
+ return list.stream().allMatch(block -> predicate.test(block.getType()));
+ }
+
+ public List getCollidingBlocks(PlayerData data) {
+
+ final List blocks = new ArrayList<>();
+
+ final int xFloor = (int) Math.floor(minX);
+ final int xCeil = (int) Math.ceil(maxX);
+
+ final int yFloor = (int) Math.floor(minY);
+ final int yCeil = (int) Math.ceil(maxY);
+
+ final int zFloor = (int) Math.floor(minZ);
+ final int zCeil = (int) Math.ceil(maxZ);
+
+ for (int x = xFloor; x < xCeil; x++) {
+
+ for (int y = yFloor; y < yCeil; y++) {
+
+ for (int z = zFloor; z < zCeil; z++) {
+
+ final Location loc = new Location(data.getDataPlayer().getWorld(), x, y, z);
+
+ if (loc.getWorld().isChunkLoaded(loc.getBlockX() >> 4, loc.getBlockZ() >> 4)) {
+ final Block b = loc.getBlock();
+ blocks.add(b);
+ }
+
+ }
+
+ }
+
+ }
+
+ return blocks;
+ }
+
+ public double getCenterX() {
+ return (this.minX + this.maxX) / 2.0;
+ }
+
+ public double getCenterY() {
+ return (this.minY + this.maxY) / 2.0;
+ }
+
+ public double getCenterZ() {
+ return (this.minZ + this.maxZ) / 2.0;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+}
+
+final class BlockPosition {
+
+ private int x, y, z;
+
+
+ public BlockPosition(final int x, final int y, final int z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public void setX(final int x) {
+ this.x = x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public void setY(final int y) {
+ this.y = y;
+ }
+
+ public int getZ() {
+ return z;
+ }
+
+ public void setZ(final int z) {
+ this.z = z;
+ }
+
+}
diff --git a/src/main/java/me/liwk/karhu/util/CollisionBox.java b/src/main/java/me/liwk/karhu/util/CollisionBox.java
new file mode 100644
index 0000000..769cb34
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/CollisionBox.java
@@ -0,0 +1,11 @@
+package me.liwk.karhu.util;
+
+import java.util.List;
+
+public interface CollisionBox {
+ boolean isCollided(CollisionBox other);
+ CollisionBox copy();
+ CollisionBox offset(double x, double y, double z);
+ void downCast(List list);
+ boolean isNull();
+}
diff --git a/src/main/java/me/liwk/karhu/util/CustomLocation.java b/src/main/java/me/liwk/karhu/util/CustomLocation.java
new file mode 100644
index 0000000..9075d46
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/CustomLocation.java
@@ -0,0 +1,108 @@
+package me.liwk.karhu.util;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.util.Vector;
+
+public class CustomLocation {
+ public double x, y, z;
+ public float yaw, pitch;
+ private long timeStamp;
+
+ public CustomLocation(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ timeStamp = System.currentTimeMillis();
+ }
+
+ public CustomLocation(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;
+
+ timeStamp = System.currentTimeMillis();
+ }
+
+ public CustomLocation(double x, double y, double z, float yaw, float pitch, long timeStamp) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.yaw = yaw;
+ this.pitch = pitch;
+ this.timeStamp = timeStamp;
+ }
+
+ public CustomLocation(Location loc) {
+ this.x = loc.getX();
+ this.y = loc.getY();
+ this.z = loc.getZ();
+ this.yaw = loc.getYaw();
+ this.pitch = loc.getPitch();
+
+ this.timeStamp = System.currentTimeMillis();
+ }
+
+ public CustomLocation clone() {
+ return new CustomLocation(x, y, z, yaw, pitch, timeStamp);
+ }
+
+ public Location toLocation(World world) {
+ return new Location(world, x, y, z, yaw, pitch);
+ }
+
+ public Vector toVector() {
+ return new Vector(x, y, z);
+ }
+
+ public double getX() {
+ return x;
+ }
+
+ public void setX(double x) {
+ this.x = x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ public void setY(double y) {
+ this.y = y;
+ }
+
+ public double getZ() {
+ return z;
+ }
+
+ public void setZ(double z) {
+ this.z = z;
+ }
+
+ public float getYaw() {
+ return yaw;
+ }
+
+ public void setYaw(float yaw) {
+ this.yaw = yaw;
+ }
+
+ public float getPitch() {
+ return pitch;
+ }
+
+ public void setPitch(float pitch) {
+ this.pitch = pitch;
+ }
+
+ public long getTimeStamp() {
+ return timeStamp;
+ }
+
+ public void setTimeStamp(long timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/util/CustomLocation2.java b/src/main/java/me/liwk/karhu/util/CustomLocation2.java
new file mode 100644
index 0000000..d1e1fbd
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/CustomLocation2.java
@@ -0,0 +1,61 @@
+package me.liwk.karhu.util;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.util.Vector;
+
+public class CustomLocation2 {
+ public double x;
+ public double y;
+ public double z;
+ public float yaw;
+ public float pitch;
+ public long timeStamp;
+
+ public CustomLocation2(double x, double y, double z, float yaw, float pitch, long timeStamp) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.yaw = yaw;
+ this.pitch = pitch;
+ this.timeStamp = timeStamp;
+ }
+
+ public CustomLocation2(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;
+ this.timeStamp = System.currentTimeMillis();
+ }
+
+ public CustomLocation2(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.timeStamp = System.currentTimeMillis();
+ }
+
+ public CustomLocation2(Location location) {
+ this.x = location.getX();
+ this.y = location.getY();
+ this.z = location.getZ();
+ this.yaw = location.getYaw();
+ this.pitch = location.getPitch();
+ this.timeStamp = System.currentTimeMillis();
+ }
+
+ public Vector toVector() {
+ return new Vector(this.x, this.y, this.z);
+ }
+
+ public Location toLocation(World world) {
+ return new Location(world, this.x, this.y, this.z, this.yaw, this.pitch);
+ }
+
+ public CustomLocation2 clone() {
+ return new CustomLocation2(this.x, this.y, this.z, this.yaw, this.pitch, this.timeStamp);
+ }
+}
+
diff --git a/src/main/java/me/liwk/karhu/util/EnumFacing.java b/src/main/java/me/liwk/karhu/util/EnumFacing.java
new file mode 100644
index 0000000..86a076a
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/EnumFacing.java
@@ -0,0 +1,603 @@
+package me.liwk.karhu.util;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Maps;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+
+public enum EnumFacing implements IStringSerializable
+{
+ DOWN("DOWN", 0, 0, 1, -1, "down", EnumFacing.AxisDirection.NEGATIVE, EnumFacing.Axis.Y, new Vec3i(0, -1, 0)),
+ UP("UP", 1, 1, 0, -1, "up", EnumFacing.AxisDirection.POSITIVE, EnumFacing.Axis.Y, new Vec3i(0, 1, 0)),
+ NORTH("NORTH", 2, 2, 3, 2, "north", EnumFacing.AxisDirection.NEGATIVE, EnumFacing.Axis.Z, new Vec3i(0, 0, -1)),
+ SOUTH("SOUTH", 3, 3, 2, 0, "south", EnumFacing.AxisDirection.POSITIVE, EnumFacing.Axis.Z, new Vec3i(0, 0, 1)),
+ WEST("WEST", 4, 4, 5, 1, "west", EnumFacing.AxisDirection.NEGATIVE, EnumFacing.Axis.X, new Vec3i(-1, 0, 0)),
+ EAST("EAST", 5, 5, 4, 3, "east", EnumFacing.AxisDirection.POSITIVE, EnumFacing.Axis.X, new Vec3i(1, 0, 0));
+
+ /** Ordering index for D-U-N-S-W-E */
+ private final int index;
+
+ /** Index of the opposite Facing in the VALUES array */
+ private final int opposite;
+
+ /** Ordering index for the HORIZONTALS field (S-W-N-E) */
+ private final int horizontalIndex;
+ private final String name;
+ private final EnumFacing.Axis axis;
+ private final EnumFacing.AxisDirection axisDirection;
+
+ /** Normalized Vector that points in the direction of this Facing */
+ private final Vec3i directionVec;
+
+ /** All facings in D-U-N-S-W-E order */
+ public static final EnumFacing[] VALUES = new EnumFacing[6];
+
+ /** All Facings with horizontal axis in order S-W-N-E */
+ private static final EnumFacing[] HORIZONTALS = new EnumFacing[4];
+ private static final Map NAME_LOOKUP = Maps.newHashMap();
+ private static final EnumFacing[] $VALUES = new EnumFacing[]{DOWN, UP, NORTH, SOUTH, WEST, EAST};
+ private static final String __OBFID = "CL_00001201";
+
+ private EnumFacing(String p_i17_3_, int p_i17_4_, int p_i17_5_, int p_i17_6_, int p_i17_7_, String p_i17_8_, EnumFacing.AxisDirection p_i17_9_, EnumFacing.Axis p_i17_10_, Vec3i p_i17_11_)
+ {
+ this.index = p_i17_5_;
+ this.horizontalIndex = p_i17_7_;
+ this.opposite = p_i17_6_;
+ this.name = p_i17_8_;
+ this.axis = p_i17_10_;
+ this.axisDirection = p_i17_9_;
+ this.directionVec = p_i17_11_;
+ }
+
+ /**
+ * Get the Index of this Facing (0-5). The order is D-U-N-S-W-E
+ */
+ public int getIndex()
+ {
+ return this.index;
+ }
+
+ /**
+ * Get the index of this horizontal facing (0-3). The order is S-W-N-E
+ */
+ public int getHorizontalIndex()
+ {
+ return this.horizontalIndex;
+ }
+
+ /**
+ * Get the AxisDirection of this Facing.
+ */
+ public EnumFacing.AxisDirection getAxisDirection()
+ {
+ return this.axisDirection;
+ }
+
+ /**
+ * Get the opposite Facing (e.g. DOWN => UP)
+ */
+ public EnumFacing getOpposite()
+ {
+ return VALUES[this.opposite];
+ }
+
+ /**
+ * Rotate this Facing around the given axis clockwise. If this facing cannot be rotated around the given axis,
+ * returns this facing without rotating.
+ */
+ public EnumFacing rotateAround(EnumFacing.Axis axis)
+ {
+ switch (EnumFacing.EnumFacing$1.field_179515_a[axis.ordinal()])
+ {
+ case 1:
+ if (this != WEST && this != EAST)
+ {
+ return this.rotateX();
+ }
+
+ return this;
+
+ case 2:
+ if (this != UP && this != DOWN)
+ {
+ return this.rotateY();
+ }
+
+ return this;
+
+ case 3:
+ if (this != NORTH && this != SOUTH)
+ {
+ return this.rotateZ();
+ }
+
+ return this;
+
+ default:
+ throw new IllegalStateException("Unable to get CW facing for axis " + axis);
+ }
+ }
+
+ /**
+ * Rotate this Facing around the Y axis clockwise (NORTH => EAST => SOUTH => WEST => NORTH)
+ */
+ public EnumFacing rotateY()
+ {
+ switch (EnumFacing.EnumFacing$1.field_179513_b[this.ordinal()])
+ {
+ case 1:
+ return EAST;
+
+ case 2:
+ return SOUTH;
+
+ case 3:
+ return WEST;
+
+ case 4:
+ return NORTH;
+
+ default:
+ throw new IllegalStateException("Unable to get Y-rotated facing of " + this);
+ }
+ }
+
+ /**
+ * Rotate this Facing around the X axis (NORTH => DOWN => SOUTH => UP => NORTH)
+ */
+ private EnumFacing rotateX()
+ {
+ switch (EnumFacing.EnumFacing$1.field_179513_b[this.ordinal()])
+ {
+ case 1:
+ return DOWN;
+
+ case 2:
+ case 4:
+ default:
+ throw new IllegalStateException("Unable to get X-rotated facing of " + this);
+
+ case 3:
+ return UP;
+
+ case 5:
+ return NORTH;
+
+ case 6:
+ return SOUTH;
+ }
+ }
+
+ /**
+ * Rotate this Facing around the Z axis (EAST => DOWN => WEST => UP => EAST)
+ */
+ private EnumFacing rotateZ()
+ {
+ switch (EnumFacing.EnumFacing$1.field_179513_b[this.ordinal()])
+ {
+ case 2:
+ return DOWN;
+
+ case 3:
+ default:
+ throw new IllegalStateException("Unable to get Z-rotated facing of " + this);
+
+ case 4:
+ return UP;
+
+ case 5:
+ return EAST;
+
+ case 6:
+ return WEST;
+ }
+ }
+
+ /**
+ * Rotate this Facing around the Y axis counter-clockwise (NORTH => WEST => SOUTH => EAST => NORTH)
+ */
+ public EnumFacing rotateYCCW()
+ {
+ switch (EnumFacing.EnumFacing$1.field_179513_b[this.ordinal()])
+ {
+ case 1:
+ return WEST;
+
+ case 2:
+ return NORTH;
+
+ case 3:
+ return EAST;
+
+ case 4:
+ return SOUTH;
+
+ default:
+ throw new IllegalStateException("Unable to get CCW facing of " + this);
+ }
+ }
+
+ /**
+ * Returns a offset that addresses the block in front of this facing.
+ */
+ public int getFrontOffsetX()
+ {
+ return this.axis == EnumFacing.Axis.X ? this.axisDirection.getOffset() : 0;
+ }
+
+ public int getFrontOffsetY()
+ {
+ return this.axis == EnumFacing.Axis.Y ? this.axisDirection.getOffset() : 0;
+ }
+
+ /**
+ * Returns a offset that addresses the block in front of this facing.
+ */
+ public int getFrontOffsetZ()
+ {
+ return this.axis == EnumFacing.Axis.Z ? this.axisDirection.getOffset() : 0;
+ }
+
+ /**
+ * Same as getName, but does not override the method from Enum.
+ */
+ public String getName2()
+ {
+ return this.name;
+ }
+
+ public EnumFacing.Axis getAxis()
+ {
+ return this.axis;
+ }
+
+ /**
+ * Get the facing specified by the given name
+ */
+ public static EnumFacing byName(String name)
+ {
+ return name == null ? null : (EnumFacing)NAME_LOOKUP.get(name.toLowerCase());
+ }
+
+ /**
+ * Get a Facing by it's index (0-5). The order is D-U-N-S-W-E. Named getFront for legacy reasons.
+ */
+ public static EnumFacing getFront(int index)
+ {
+ return VALUES[MathHelper.abs_int(index % VALUES.length)];
+ }
+
+ /**
+ * Get a Facing by it's horizontal index (0-3). The order is S-W-N-E.
+ */
+ public static EnumFacing getHorizontal(int p_176731_0_)
+ {
+ return HORIZONTALS[MathHelper.abs_int(p_176731_0_ % HORIZONTALS.length)];
+ }
+
+ /**
+ * Get the Facing corresponding to the given angle (0-360). An angle of 0 is SOUTH, an angle of 90 would be WEST.
+ */
+ public static EnumFacing fromAngle(double angle)
+ {
+ return getHorizontal(MathHelper.floor_double(angle / 90.0D + 0.5D) & 3);
+ }
+
+ /**
+ * Choose a random Facing using the given Random
+ */
+ public static EnumFacing random(Random rand)
+ {
+ return values()[rand.nextInt(values().length)];
+ }
+
+ public static EnumFacing getFacingFromVector(float p_176737_0_, float p_176737_1_, float p_176737_2_)
+ {
+ EnumFacing enumfacing = NORTH;
+ float f = Float.MIN_VALUE;
+
+ for (EnumFacing enumfacing1 : values())
+ {
+ float f1 = p_176737_0_ * (float)enumfacing1.directionVec.getX() + p_176737_1_ * (float)enumfacing1.directionVec.getY() + p_176737_2_ * (float)enumfacing1.directionVec.getZ();
+
+ if (f1 > f)
+ {
+ f = f1;
+ enumfacing = enumfacing1;
+ }
+ }
+
+ return enumfacing;
+ }
+
+ public String toString()
+ {
+ return this.name;
+ }
+
+ public String getName()
+ {
+ return this.name;
+ }
+
+ public static EnumFacing func_181076_a(EnumFacing.AxisDirection p_181076_0_, EnumFacing.Axis p_181076_1_)
+ {
+ for (EnumFacing enumfacing : values())
+ {
+ if (enumfacing.getAxisDirection() == p_181076_0_ && enumfacing.getAxis() == p_181076_1_)
+ {
+ return enumfacing;
+ }
+ }
+
+ throw new IllegalArgumentException("No such direction: " + p_181076_0_ + " " + p_181076_1_);
+ }
+
+ /**
+ * Get a normalized Vector that points in the direction of this Facing.
+ */
+ public Vec3i getDirectionVec()
+ {
+ return this.directionVec;
+ }
+
+ static {
+ for (EnumFacing enumfacing : values())
+ {
+ VALUES[enumfacing.index] = enumfacing;
+
+ if (enumfacing.getAxis().isHorizontal())
+ {
+ HORIZONTALS[enumfacing.horizontalIndex] = enumfacing;
+ }
+
+ NAME_LOOKUP.put(enumfacing.getName2().toLowerCase(), enumfacing);
+ }
+ }
+
+ static final class EnumFacing$1 {
+ static final int[] field_179515_a;
+ static final int[] field_179513_b;
+ static final int[] field_179514_c = new int[EnumFacing.Plane.values().length];
+ private static final String __OBFID = "CL_00002322";
+
+ static {
+ try {
+ field_179514_c[EnumFacing.Plane.HORIZONTAL.ordinal()] = 1;
+ }
+ catch (NoSuchFieldError var11)
+ {
+ ;
+ }
+
+ try {
+ field_179514_c[EnumFacing.Plane.VERTICAL.ordinal()] = 2;
+ }
+ catch (NoSuchFieldError var10)
+ {
+ ;
+ }
+
+ field_179513_b = new int[EnumFacing.values().length];
+
+ try {
+ field_179513_b[EnumFacing.NORTH.ordinal()] = 1;
+ }
+ catch (NoSuchFieldError var9)
+ {
+ ;
+ }
+
+ try {
+ field_179513_b[EnumFacing.EAST.ordinal()] = 2;
+ }
+ catch (NoSuchFieldError var8)
+ {
+ ;
+ }
+
+ try {
+ field_179513_b[EnumFacing.SOUTH.ordinal()] = 3;
+ }
+ catch (NoSuchFieldError var7)
+ {
+ ;
+ }
+
+ try {
+ field_179513_b[EnumFacing.WEST.ordinal()] = 4;
+ }
+ catch (NoSuchFieldError var6)
+ {
+ ;
+ }
+
+ try {
+ field_179513_b[EnumFacing.UP.ordinal()] = 5;
+ }
+ catch (NoSuchFieldError var5)
+ {
+ ;
+ }
+
+ try {
+ field_179513_b[EnumFacing.DOWN.ordinal()] = 6;
+ }
+ catch (NoSuchFieldError var4)
+ {
+ ;
+ }
+
+ field_179515_a = new int[EnumFacing.Axis.values().length];
+
+ try {
+ field_179515_a[EnumFacing.Axis.X.ordinal()] = 1;
+ }
+ catch (NoSuchFieldError var3)
+ {
+ ;
+ }
+
+ try {
+ field_179515_a[EnumFacing.Axis.Y.ordinal()] = 2;
+ }
+ catch (NoSuchFieldError var2)
+ {
+ ;
+ }
+
+ try {
+ field_179515_a[EnumFacing.Axis.Z.ordinal()] = 3;
+ }
+ catch (NoSuchFieldError var1)
+ {
+ ;
+ }
+ }
+ }
+
+ public static enum Axis implements Predicate, IStringSerializable {
+ X("X", 0, "x", EnumFacing.Plane.HORIZONTAL),
+ Y("Y", 1, "y", EnumFacing.Plane.VERTICAL),
+ Z("Z", 2, "z", EnumFacing.Plane.HORIZONTAL);
+
+ private static final Map NAME_LOOKUP = Maps.newHashMap();
+ private final String name;
+ private final EnumFacing.Plane plane;
+ private static final EnumFacing.Axis[] $VALUES = new EnumFacing.Axis[]{X, Y, Z};
+ private static final String __OBFID = "CL_00002321";
+
+ private Axis(String p_i14_3_, int p_i14_4_, String p_i14_5_, EnumFacing.Plane p_i14_6_)
+ {
+ this.name = p_i14_5_;
+ this.plane = p_i14_6_;
+ }
+
+ public static EnumFacing.Axis byName(String name)
+ {
+ return name == null ? null : (EnumFacing.Axis)NAME_LOOKUP.get(name.toLowerCase());
+ }
+
+ public String getName2()
+ {
+ return this.name;
+ }
+
+ public boolean isVertical()
+ {
+ return this.plane == EnumFacing.Plane.VERTICAL;
+ }
+
+ public boolean isHorizontal()
+ {
+ return this.plane == EnumFacing.Plane.HORIZONTAL;
+ }
+
+ public String toString()
+ {
+ return this.name;
+ }
+
+ public boolean apply(EnumFacing p_apply_1_)
+ {
+ return p_apply_1_ != null && p_apply_1_.getAxis() == this;
+ }
+
+ public EnumFacing.Plane getPlane()
+ {
+ return this.plane;
+ }
+
+ public String getName()
+ {
+ return this.name;
+ }
+
+ public boolean apply(Object p_apply_1_)
+ {
+ return this.apply((EnumFacing)p_apply_1_);
+ }
+
+ static {
+ for (EnumFacing.Axis enumfacing$axis : values())
+ {
+ NAME_LOOKUP.put(enumfacing$axis.getName2().toLowerCase(), enumfacing$axis);
+ }
+ }
+ }
+
+ public static enum AxisDirection {
+ POSITIVE("POSITIVE", 0, 1, "Towards positive"),
+ NEGATIVE("NEGATIVE", 1, -1, "Towards negative");
+
+ private final int offset;
+ private final String description;
+ private static final EnumFacing.AxisDirection[] $VALUES = new EnumFacing.AxisDirection[]{POSITIVE, NEGATIVE};
+ private static final String __OBFID = "CL_00002320";
+
+ private AxisDirection(String p_i15_3_, int p_i15_4_, int p_i15_5_, String p_i15_6_)
+ {
+ this.offset = p_i15_5_;
+ this.description = p_i15_6_;
+ }
+
+ public int getOffset()
+ {
+ return this.offset;
+ }
+
+ public String toString()
+ {
+ return this.description;
+ }
+ }
+
+ public static enum Plane implements Predicate, Iterable {
+ HORIZONTAL("HORIZONTAL", 0),
+ VERTICAL("VERTICAL", 1);
+
+ private static final EnumFacing.Plane[] $VALUES = new EnumFacing.Plane[]{HORIZONTAL, VERTICAL};
+ private static final String __OBFID = "CL_00002319";
+
+ private Plane(String p_i16_3_, int p_i16_4_)
+ {
+ }
+
+ public EnumFacing[] facings()
+ {
+ switch (EnumFacing.EnumFacing$1.field_179514_c[this.ordinal()])
+ {
+ case 1:
+ return new EnumFacing[] {EnumFacing.NORTH, EnumFacing.EAST, EnumFacing.SOUTH, EnumFacing.WEST};
+ case 2:
+ return new EnumFacing[] {EnumFacing.UP, EnumFacing.DOWN};
+ default:
+ throw new Error("Someone\'s been tampering with the universe!");
+ }
+ }
+
+ public EnumFacing random(Random rand)
+ {
+ EnumFacing[] aenumfacing = this.facings();
+ return aenumfacing[rand.nextInt(aenumfacing.length)];
+ }
+
+ public boolean apply(EnumFacing p_apply_1_)
+ {
+ return p_apply_1_ != null && p_apply_1_.getAxis().getPlane() == this;
+ }
+
+ public Iterator iterator()
+ {
+ return Iterators.forArray(this.facings());
+ }
+
+ public boolean apply(Object p_apply_1_)
+ {
+ return this.apply((EnumFacing)p_apply_1_);
+ }
+ }
+}
+
diff --git a/src/main/java/me/liwk/karhu/util/IStringSerializable.java b/src/main/java/me/liwk/karhu/util/IStringSerializable.java
new file mode 100644
index 0000000..08337f2
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/IStringSerializable.java
@@ -0,0 +1,5 @@
+package me.liwk.karhu.util;
+
+public interface IStringSerializable {
+ String getName();
+}
diff --git a/src/main/java/me/liwk/karhu/util/KarhuStream.java b/src/main/java/me/liwk/karhu/util/KarhuStream.java
new file mode 100644
index 0000000..0e568eb
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/KarhuStream.java
@@ -0,0 +1,40 @@
+package me.liwk.karhu.util;
+
+import io.github.retrooper.packetevents.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+/**
+ * Mostly just utility, i will look into caching methods and so on
+ * for performance boosting when using the streams but i dont know yet lol
+ */
+public final class KarhuStream {
+
+ private final Supplier> s;
+ private final boolean parallel;
+
+ private final Collection c;
+
+ public KarhuStream(List l, boolean parallel) {
+ this.s = l::stream;
+ this.c = l;
+ this.parallel = parallel;
+ }
+
+ public boolean any(Predicate t) {
+ return this.parallel ? this.s.get().parallel().anyMatch(t) : this.s.get().anyMatch(t);
+ }
+
+ public boolean all(Predicate t) {
+ return this.parallel ? this.s.get().parallel().allMatch(t) : this.s.get().allMatch(t);
+ }
+
+ public @NotNull Collection getCollection() {
+ return c;
+ }
+
+}
diff --git a/src/main/java/me/liwk/karhu/util/LocationData.java b/src/main/java/me/liwk/karhu/util/LocationData.java
new file mode 100644
index 0000000..c41f568
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/LocationData.java
@@ -0,0 +1,37 @@
+package me.liwk.karhu.util;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter @Setter
+public class LocationData {
+ private static final double HITBOX_SIZE = 0.3;
+
+ private double x, y, z;
+ private float yaw, pitch;
+
+ private final double minX, maxX;
+ private final double minZ, maxZ;
+
+ private final long time = System.currentTimeMillis();
+
+ public LocationData(double x, double y, double z, float yaw, float pitch) {
+ this.x = x;
+ minX = x - HITBOX_SIZE;
+ maxX = x + HITBOX_SIZE;
+
+ this.y = y;
+
+ this.z = z;
+ minZ = z - HITBOX_SIZE;
+ maxZ = z + HITBOX_SIZE;
+
+ this.yaw = yaw;
+ this.pitch = pitch;
+ }
+
+ public Vec3 toVector() {
+ return new Vec3(this.x, this.y, this.z);
+ }
+
+}
diff --git a/src/main/java/me/liwk/karhu/util/MathAPI.java b/src/main/java/me/liwk/karhu/util/MathAPI.java
new file mode 100644
index 0000000..20c96a3
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/MathAPI.java
@@ -0,0 +1,178 @@
+package me.liwk.karhu.util;
+
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.Plugin;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.UUID;
+
+public class MathAPI {
+
+ private String licenseKey;
+ private Plugin plugin;
+ private String validationServer;
+ private LogType logType = LogType.NORMAL;
+ private String securityKey = "YecoF0I6M05thxLeokoHuW8iUhTdIUInjkfF";
+ private boolean debug = false;
+ private boolean idfk = false;
+
+ public MathAPI(String licenseKey, String validationServer, Plugin plugin) {
+ this.licenseKey = licenseKey;
+ this.plugin = plugin;
+ this.validationServer = validationServer;
+ }
+
+ public MathAPI setSecurityKey(String securityKey) {
+ this.securityKey = securityKey;
+ return this;
+ }
+
+ public MathAPI setConsoleLog(LogType logType) {
+ this.logType = logType;
+ return this;
+ }
+
+ public MathAPI debug() {
+ debug = true;
+ return this;
+ }
+
+ public boolean register() {
+ log(0, "*--------------------------------*");
+ log(0, "Valid purchaser check");
+ ValidationType vt = isValid();
+ if (vt == ValidationType.VALID) {
+ idfk = true;
+ log(1, "Great, you are a valid purchaser (or are you :O)");
+ log(0, "*--------------------------------*");
+ return true;
+ } else {
+ log(1, "Walter is sad, :( if you did purchase this look at the mc-market page and get your license key");
+ log(1, "Failed because of " + vt.toString());
+ log(1, "Disabling Karhu :(");
+ log(0, "*--------------------------------*");
+
+ Bukkit.getScheduler().cancelTasks(plugin);
+ Bukkit.getPluginManager().disablePlugin(plugin);
+ return false;
+ }
+ }
+
+ public boolean isValidSimple() {
+ return (isValid() == ValidationType.VALID);
+ }
+
+ private String requestServer(String v1, String v2) throws IOException {
+ URL url = new URL(validationServer + "?v1=" + v1 + "&v2=" + v2 + "&pl=" + plugin.getName());
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.setRequestMethod("GET");
+ con.setRequestProperty("User-Agent", "Mozilla/5.0");
+
+ int responseCode = con.getResponseCode();
+ if (debug) {
+ System.out.println("\nSending 'GET' request to URL : " + url);
+ System.out.println("Response Code : " + responseCode);
+ }
+
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()))) {
+ String inputLine;
+ StringBuilder response = new StringBuilder();
+
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+
+ return response.toString();
+ }
+ }
+
+ public ValidationType isValid() {
+ String rand = toBinary(UUID.randomUUID().toString());
+ String sKey = toBinary(securityKey);
+ String key = toBinary(licenseKey);
+
+ try {
+ String response = requestServer(xor(rand, sKey), xor(rand, key));
+
+ if (response.startsWith("<")) {
+ log(1, "The License-Server returned an invalid response!");
+ log(1, "In most cases this is caused by:");
+ log(1, "1) Your Web-Host injects JS into the page (often caused by free hosts)");
+ log(1, "2) Your ValidationServer-URL is wrong");
+ log(1,
+ "SERVER-RESPONSE: " + (response.length() < 150 || debug ? response : response.substring(0, 150) + "..."));
+ return ValidationType.PAGE_ERROR;
+ }
+
+ try {
+ return ValidationType.valueOf(response);
+ } catch (IllegalArgumentException exc) {
+ String respRand = xor(xor(response, key), sKey);
+ if (rand.substring(0, respRand.length()).equals(respRand))
+ return ValidationType.VALID;
+ else
+ return ValidationType.WRONG_RESPONSE;
+ }
+ } catch (IOException e) {
+ if (debug)
+ e.printStackTrace();
+ return ValidationType.PAGE_ERROR;
+ }
+ }
+
+ //
+ // Cryptographic
+ //
+
+ private static String xor(String s1, String s2) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < (Math.min(s1.length(), s2.length())); i++)
+ result.append(Byte.parseByte("" + s1.charAt(i)) ^ Byte.parseByte(s2.charAt(i) + ""));
+ return result.toString();
+ }
+
+ //
+ // Enums
+ //
+
+ public enum LogType {
+ NORMAL, LOW, NONE;
+ }
+
+ public enum ValidationType {
+ WRONG_RESPONSE, PAGE_ERROR, URL_ERROR, KEY_OUTDATED, KEY_NOT_FOUND, NOT_VALID_IP, INVALID_PLUGIN, VALID;
+ }
+
+ //
+ // Binary methods
+ //
+
+ private String toBinary(String s) {
+ byte[] bytes = s.getBytes();
+ StringBuilder binary = new StringBuilder();
+ for (byte b : bytes) {
+ int val = b;
+ for (int i = 0; i < 8; i++) {
+ binary.append((val & 128) == 0 ? 0 : 1);
+ val <<= 1;
+ }
+ }
+ return binary.toString();
+ }
+
+ //
+ // Console-Log
+ //
+
+ private void log(int type, String message) {
+ if (logType == LogType.NONE || (logType == LogType.LOW && type == 0))
+ return;
+ System.out.println(message);
+ }
+}
+
+
diff --git a/src/main/java/me/liwk/karhu/util/MathHelper.java b/src/main/java/me/liwk/karhu/util/MathHelper.java
new file mode 100644
index 0000000..cdaa03c
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/MathHelper.java
@@ -0,0 +1,592 @@
+package me.liwk.karhu.util;
+
+import java.util.Random;
+import java.util.UUID;
+
+public class MathHelper
+{
+ public static final float SQRT_2 = sqrt_float(2.0F);
+ private static final int SIN_BITS = 12;
+ private static final int SIN_MASK = 4095;
+ private static final int SIN_COUNT = 4096;
+ public static final float PI = (float)Math.PI;
+ public static final float PI2 = ((float)Math.PI * 2F);
+ public static final float PId2 = ((float)Math.PI / 2F);
+ private static final float radFull = ((float)Math.PI * 2F);
+ private static final float degFull = 360.0F;
+ private static final float radToIndex = 651.8986F;
+ private static final float degToIndex = 11.377778F;
+ public static final float deg2Rad = 0.017453292F;
+ private static final float[] SIN_TABLE_FAST = new float[4096];
+ public static boolean fastMath = false;
+
+ /**
+ * A table of sin values computed from 0 (inclusive) to 2*pi (exclusive), with steps of 2*PI / 65536.
+ */
+ private static final float[] SIN_TABLE = new float[65536];
+
+ /**
+ * Though it looks like an array, this is really more like a mapping. Key (index of this array) is the upper 5 bits
+ * of the result of multiplying a 32-bit unsigned integer by the B(2, 5) De Bruijn sequence 0x077CB531. Value
+ * (value stored in the array) is the unique index (from the right) of the leftmost one-bit in a 32-bit unsigned
+ * integer that can cause the upper 5 bits to get that value. Used for highly optimized "find the log-base-2 of
+ * this number" calculations.
+ */
+ private static final int[] multiplyDeBruijnBitPosition;
+ private static final double field_181163_d;
+ private static final double[] field_181164_e;
+ private static final double[] field_181165_f;
+ private static final String __OBFID = "CL_00001496";
+
+ /**
+ * sin looked up in a table
+ */
+ public static float sin(float p_76126_0_)
+ {
+ return fastMath ? SIN_TABLE_FAST[(int)(p_76126_0_ * 651.8986F) & 4095] : SIN_TABLE[(int)(p_76126_0_ * 10430.378F) & 65535];
+ }
+
+ /**
+ * cos looked up in the sin table with the appropriate offset
+ */
+ public static float cos(float value)
+ {
+ return fastMath ? SIN_TABLE_FAST[(int)((value + ((float)Math.PI / 2F)) * 651.8986F) & 4095] : SIN_TABLE[(int)(value * 10430.378F + 16384.0F) & 65535];
+ }
+
+ public static float sqrt_float(float value)
+ {
+ return (float)Math.sqrt((double)value);
+ }
+
+ public static float sqrt_double(double value)
+ {
+ return (float)Math.sqrt(value);
+ }
+
+ /**
+ * Returns the greatest integer less than or equal to the float argument
+ */
+ public static int floor_float(float value)
+ {
+ int i = (int)value;
+ return value < (float)i ? i - 1 : i;
+ }
+
+ /**
+ * returns par0 cast as an int, and no greater than Integer.MAX_VALUE-1024
+ */
+ public static int truncateDoubleToInt(double value)
+ {
+ return (int)(value + 1024.0D) - 1024;
+ }
+
+ /**
+ * Returns the greatest integer less than or equal to the double argument
+ */
+ public static int floor_double(double value)
+ {
+ int i = (int)value;
+ return value < (double)i ? i - 1 : i;
+ }
+
+ /**
+ * Long version of floor_double
+ */
+ public static long floor_double_long(double value)
+ {
+ long i = (long)value;
+ return value < (double)i ? i - 1L : i;
+ }
+
+ public static float sqrt(double var0) {
+ return (float)Math.sqrt(var0);
+ }
+
+ public static int func_154353_e(double value)
+ {
+ return (int)(value >= 0.0D ? value : -value + 1.0D);
+ }
+
+ public static float abs(float value)
+ {
+ return value >= 0.0F ? value : -value;
+ }
+
+ /**
+ * Returns the unsigned value of an int.
+ */
+ public static int abs_int(int value)
+ {
+ return value >= 0 ? value : -value;
+ }
+
+ public static int ceiling_float_int(float value)
+ {
+ int i = (int)value;
+ return value > (float)i ? i + 1 : i;
+ }
+
+ public static int ceiling_double_int(double value)
+ {
+ int i = (int)value;
+ return value > (double)i ? i + 1 : i;
+ }
+
+ /**
+ * Returns the value of the first parameter, clamped to be within the lower and upper limits given by the second and
+ * third parameters.
+ */
+ public static int clamp_int(int num, int min, int max)
+ {
+ return num < min ? min : (num > max ? max : num);
+ }
+
+ /**
+ * Returns the value of the first parameter, clamped to be within the lower and upper limits given by the second and
+ * third parameters
+ */
+ public static float clamp_float(float num, float min, float max)
+ {
+ return num < min ? min : (num > max ? max : num);
+ }
+
+ public static double clamp_double(double num, double min, double max)
+ {
+ return num < min ? min : (num > max ? max : num);
+ }
+
+ public static double denormalizeClamp(double p_151238_0_, double p_151238_2_, double p_151238_4_)
+ {
+ return p_151238_4_ < 0.0D ? p_151238_0_ : (p_151238_4_ > 1.0D ? p_151238_2_ : p_151238_0_ + (p_151238_2_ - p_151238_0_) * p_151238_4_);
+ }
+
+ /**
+ * Maximum of the absolute value of two numbers.
+ */
+ public static double abs_max(double p_76132_0_, double p_76132_2_)
+ {
+ if (p_76132_0_ < 0.0D)
+ {
+ p_76132_0_ = -p_76132_0_;
+ }
+
+ if (p_76132_2_ < 0.0D)
+ {
+ p_76132_2_ = -p_76132_2_;
+ }
+
+ return p_76132_0_ > p_76132_2_ ? p_76132_0_ : p_76132_2_;
+ }
+
+ /**
+ * Buckets an integer with specifed bucket sizes. Args: i, bucketSize
+ */
+ public static int bucketInt(int p_76137_0_, int p_76137_1_)
+ {
+ return p_76137_0_ < 0 ? -((-p_76137_0_ - 1) / p_76137_1_) - 1 : p_76137_0_ / p_76137_1_;
+ }
+
+ public static int getRandomIntegerInRange(Random p_76136_0_, int p_76136_1_, int p_76136_2_)
+ {
+ return p_76136_1_ >= p_76136_2_ ? p_76136_1_ : p_76136_0_.nextInt(p_76136_2_ - p_76136_1_ + 1) + p_76136_1_;
+ }
+
+ public static float randomFloatClamp(Random p_151240_0_, float p_151240_1_, float p_151240_2_)
+ {
+ return p_151240_1_ >= p_151240_2_ ? p_151240_1_ : p_151240_0_.nextFloat() * (p_151240_2_ - p_151240_1_) + p_151240_1_;
+ }
+
+ public static double getRandomDoubleInRange(Random p_82716_0_, double p_82716_1_, double p_82716_3_)
+ {
+ return p_82716_1_ >= p_82716_3_ ? p_82716_1_ : p_82716_0_.nextDouble() * (p_82716_3_ - p_82716_1_) + p_82716_1_;
+ }
+
+ public static double average(long[] values)
+ {
+ long i = 0L;
+
+ for (long j : values)
+ {
+ i += j;
+ }
+
+ return (double)i / (double)values.length;
+ }
+
+ public static boolean epsilonEquals(float p_180185_0_, float p_180185_1_)
+ {
+ return abs(p_180185_1_ - p_180185_0_) < 1.0E-5F;
+ }
+
+ public static int normalizeAngle(int p_180184_0_, int p_180184_1_)
+ {
+ return (p_180184_0_ % p_180184_1_ + p_180184_1_) % p_180184_1_;
+ }
+
+ /**
+ * the angle is reduced to an angle between -180 and +180 by mod, and a 360 check
+ */
+ public static float wrapAngleTo180_float(float value)
+ {
+ value = value % 360.0F;
+
+ if (value >= 180.0F)
+ {
+ value -= 360.0F;
+ }
+
+ if (value < -180.0F)
+ {
+ value += 360.0F;
+ }
+
+ return value;
+ }
+
+ /**
+ * the angle is reduced to an angle between -180 and +180 by mod, and a 360 check
+ */
+ public static double wrapAngleTo180_double(double value)
+ {
+ value = value % 360.0D;
+
+ if (value >= 180.0D)
+ {
+ value -= 360.0D;
+ }
+
+ if (value < -180.0D)
+ {
+ value += 360.0D;
+ }
+
+ return value;
+ }
+
+ /**
+ * parses the string as integer or returns the second parameter if it fails
+ */
+ public static int parseIntWithDefault(String p_82715_0_, int p_82715_1_)
+ {
+ try
+ {
+ return Integer.parseInt(p_82715_0_);
+ }
+ catch (Throwable var3)
+ {
+ return p_82715_1_;
+ }
+ }
+
+ /**
+ * parses the string as integer or returns the second parameter if it fails. this value is capped to par2
+ */
+ public static int parseIntWithDefaultAndMax(String p_82714_0_, int p_82714_1_, int p_82714_2_)
+ {
+ return Math.max(p_82714_2_, parseIntWithDefault(p_82714_0_, p_82714_1_));
+ }
+
+ /**
+ * parses the string as double or returns the second parameter if it fails.
+ */
+ public static double parseDoubleWithDefault(String p_82712_0_, double p_82712_1_)
+ {
+ try
+ {
+ return Double.parseDouble(p_82712_0_);
+ }
+ catch (Throwable var4)
+ {
+ return p_82712_1_;
+ }
+ }
+
+ public static double parseDoubleWithDefaultAndMax(String p_82713_0_, double p_82713_1_, double p_82713_3_)
+ {
+ return Math.max(p_82713_3_, parseDoubleWithDefault(p_82713_0_, p_82713_1_));
+ }
+
+ /**
+ * Returns the input value rounded up to the next highest power of two.
+ */
+ public static int roundUpToPowerOfTwo(int value)
+ {
+ int i = value - 1;
+ i = i | i >> 1;
+ i = i | i >> 2;
+ i = i | i >> 4;
+ i = i | i >> 8;
+ i = i | i >> 16;
+ return i + 1;
+ }
+
+ /**
+ * Is the given value a power of two? (1, 2, 4, 8, 16, ...)
+ */
+ private static boolean isPowerOfTwo(int value)
+ {
+ return value != 0 && (value & value - 1) == 0;
+ }
+
+ /**
+ * Uses a B(2, 5) De Bruijn sequence and a lookup table to efficiently calculate the log-base-two of the given
+ * value. Optimized for cases where the input value is a power-of-two. If the input value is not a power-of-two,
+ * then subtract 1 from the return value.
+ */
+ private static int calculateLogBaseTwoDeBruijn(int value)
+ {
+ value = isPowerOfTwo(value) ? value : roundUpToPowerOfTwo(value);
+ return multiplyDeBruijnBitPosition[(int)((long)value * 125613361L >> 27) & 31];
+ }
+
+ /**
+ * Efficiently calculates the floor of the base-2 log of an integer value. This is effectively the index of the
+ * highest bit that is set. For example, if the number in binary is 0...100101, this will return 5.
+ */
+ public static int calculateLogBaseTwo(int value)
+ {
+ return calculateLogBaseTwoDeBruijn(value) - (isPowerOfTwo(value) ? 0 : 1);
+ }
+
+ public static int func_154354_b(int p_154354_0_, int p_154354_1_)
+ {
+ if (p_154354_1_ == 0)
+ {
+ return 0;
+ }
+ else if (p_154354_0_ == 0)
+ {
+ return p_154354_1_;
+ }
+ else
+ {
+ if (p_154354_0_ < 0)
+ {
+ p_154354_1_ *= -1;
+ }
+
+ int i = p_154354_0_ % p_154354_1_;
+ return i == 0 ? p_154354_0_ : p_154354_0_ + p_154354_1_ - i;
+ }
+ }
+
+ public static int func_180183_b(float p_180183_0_, float p_180183_1_, float p_180183_2_)
+ {
+ return func_180181_b(floor_float(p_180183_0_ * 255.0F), floor_float(p_180183_1_ * 255.0F), floor_float(p_180183_2_ * 255.0F));
+ }
+
+ public static int func_180181_b(int p_180181_0_, int p_180181_1_, int p_180181_2_)
+ {
+ int i = (p_180181_0_ << 8) + p_180181_1_;
+ i = (i << 8) + p_180181_2_;
+ return i;
+ }
+
+ public static int func_180188_d(int p_180188_0_, int p_180188_1_)
+ {
+ int i = (p_180188_0_ & 16711680) >> 16;
+ int j = (p_180188_1_ & 16711680) >> 16;
+ int k = (p_180188_0_ & 65280) >> 8;
+ int l = (p_180188_1_ & 65280) >> 8;
+ int i1 = (p_180188_0_ & 255) >> 0;
+ int j1 = (p_180188_1_ & 255) >> 0;
+ int k1 = (int)((float)i * (float)j / 255.0F);
+ int l1 = (int)((float)k * (float)l / 255.0F);
+ int i2 = (int)((float)i1 * (float)j1 / 255.0F);
+ return p_180188_0_ & -16777216 | k1 << 16 | l1 << 8 | i2;
+ }
+
+ public static double func_181162_h(double p_181162_0_)
+ {
+ return p_181162_0_ - Math.floor(p_181162_0_);
+ }
+
+ /*public static long getPositionRandom(Vec3i pos)
+ {
+ return getCoordinateRandom(pos.getX(), pos.getY(), pos.getZ());
+ }*/
+
+ public static long getCoordinateRandom(int x, int y, int z)
+ {
+ long i = (long)(x * 3129871) ^ (long)z * 116129781L ^ (long)y;
+ i = i * i * 42317861L + i * 11L;
+ return i;
+ }
+
+ public static UUID getRandomUuid(Random rand)
+ {
+ long i = rand.nextLong() & -61441L | 16384L;
+ long j = rand.nextLong() & 4611686018427387903L | Long.MIN_VALUE;
+ return new UUID(i, j);
+ }
+
+ public static double func_181160_c(double p_181160_0_, double p_181160_2_, double p_181160_4_)
+ {
+ return (p_181160_0_ - p_181160_2_) / (p_181160_4_ - p_181160_2_);
+ }
+
+ public static double func_181159_b(double p_181159_0_, double p_181159_2_)
+ {
+ double d0 = p_181159_2_ * p_181159_2_ + p_181159_0_ * p_181159_0_;
+
+ if (Double.isNaN(d0))
+ {
+ return Double.NaN;
+ }
+ else
+ {
+ boolean flag = p_181159_0_ < 0.0D;
+
+ if (flag)
+ {
+ p_181159_0_ = -p_181159_0_;
+ }
+
+ boolean flag1 = p_181159_2_ < 0.0D;
+
+ if (flag1)
+ {
+ p_181159_2_ = -p_181159_2_;
+ }
+
+ boolean flag2 = p_181159_0_ > p_181159_2_;
+
+ if (flag2)
+ {
+ double d1 = p_181159_2_;
+ p_181159_2_ = p_181159_0_;
+ p_181159_0_ = d1;
+ }
+
+ double d9 = func_181161_i(d0);
+ p_181159_2_ = p_181159_2_ * d9;
+ p_181159_0_ = p_181159_0_ * d9;
+ double d2 = field_181163_d + p_181159_0_;
+ int i = (int)Double.doubleToRawLongBits(d2);
+ double d3 = field_181164_e[i];
+ double d4 = field_181165_f[i];
+ double d5 = d2 - field_181163_d;
+ double d6 = p_181159_0_ * d4 - p_181159_2_ * d5;
+ double d7 = (6.0D + d6 * d6) * d6 * 0.16666666666666666D;
+ double d8 = d3 + d7;
+
+ if (flag2)
+ {
+ d8 = (Math.PI / 2D) - d8;
+ }
+
+ if (flag1)
+ {
+ d8 = Math.PI - d8;
+ }
+
+ if (flag)
+ {
+ d8 = -d8;
+ }
+
+ return d8;
+ }
+ }
+
+ public static double func_181161_i(double p_181161_0_)
+ {
+ double d0 = 0.5D * p_181161_0_;
+ long i = Double.doubleToRawLongBits(p_181161_0_);
+ i = 6910469410427058090L - (i >> 1);
+ p_181161_0_ = Double.longBitsToDouble(i);
+ p_181161_0_ = p_181161_0_ * (1.5D - d0 * p_181161_0_ * p_181161_0_);
+ return p_181161_0_;
+ }
+
+ public static int func_181758_c(float p_181758_0_, float p_181758_1_, float p_181758_2_)
+ {
+ int i = (int)(p_181758_0_ * 6.0F) % 6;
+ float f = p_181758_0_ * 6.0F - (float)i;
+ float f1 = p_181758_2_ * (1.0F - p_181758_1_);
+ float f2 = p_181758_2_ * (1.0F - f * p_181758_1_);
+ float f3 = p_181758_2_ * (1.0F - (1.0F - f) * p_181758_1_);
+ float f4;
+ float f5;
+ float f6;
+
+ switch (i)
+ {
+ case 0:
+ f4 = p_181758_2_;
+ f5 = f3;
+ f6 = f1;
+ break;
+
+ case 1:
+ f4 = f2;
+ f5 = p_181758_2_;
+ f6 = f1;
+ break;
+
+ case 2:
+ f4 = f1;
+ f5 = p_181758_2_;
+ f6 = f3;
+ break;
+
+ case 3:
+ f4 = f1;
+ f5 = f2;
+ f6 = p_181758_2_;
+ break;
+
+ case 4:
+ f4 = f3;
+ f5 = f1;
+ f6 = p_181758_2_;
+ break;
+
+ case 5:
+ f4 = p_181758_2_;
+ f5 = f1;
+ f6 = f2;
+ break;
+
+ default:
+ throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + p_181758_0_ + ", " + p_181758_1_ + ", " + p_181758_2_);
+ }
+
+ int j = clamp_int((int)(f4 * 255.0F), 0, 255);
+ int k = clamp_int((int)(f5 * 255.0F), 0, 255);
+ int l = clamp_int((int)(f6 * 255.0F), 0, 255);
+ return j << 16 | k << 8 | l;
+ }
+
+ static
+ {
+ for (int i = 0; i < 65536; ++i)
+ {
+ SIN_TABLE[i] = (float)Math.sin((double)i * Math.PI * 2.0D / 65536.0D);
+ }
+
+ for (int j = 0; j < 4096; ++j)
+ {
+ SIN_TABLE_FAST[j] = (float)Math.sin((double)(((float)j + 0.5F) / 4096.0F * ((float)Math.PI * 2F)));
+ }
+
+ for (int l = 0; l < 360; l += 90)
+ {
+ SIN_TABLE_FAST[(int)((float)l * 11.377778F) & 4095] = (float)Math.sin((double)((float)l * 0.017453292F));
+ }
+
+ multiplyDeBruijnBitPosition = new int[] {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
+ field_181163_d = Double.longBitsToDouble(4805340802404319232L);
+ field_181164_e = new double[257];
+ field_181165_f = new double[257];
+
+ for (int k = 0; k < 257; ++k)
+ {
+ double d1 = (double)k / 256.0D;
+ double d0 = Math.asin(d1);
+ field_181165_f[k] = Math.cos(d0);
+ field_181164_e[k] = d0;
+ }
+ }
+}
diff --git a/src/main/java/me/liwk/karhu/util/MathUtil.java b/src/main/java/me/liwk/karhu/util/MathUtil.java
new file mode 100644
index 0000000..2bcc9a0
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/MathUtil.java
@@ -0,0 +1,713 @@
+package me.liwk.karhu.util;
+
+import lombok.val;
+import lombok.var;
+import me.liwk.karhu.Karhu;
+import me.liwk.karhu.data.PlayerData;
+import org.apache.commons.lang.ArrayUtils;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.util.Vector;
+
+import javax.annotation.Nullable;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class MathUtil {
+
+ public static final double HITBOX_NORMAL = 0.4;
+ public static final double HITBOX_DIAGONAL = Math.sqrt(Math.pow(HITBOX_NORMAL, 2) + Math.pow(HITBOX_NORMAL, 2));
+
+ public static double getRandomDouble(double maximum, double minimum) {
+ Random r = new Random();
+ return minimum + (maximum - minimum) * r.nextDouble();
+ }
+
+ public static double average(final Iterable extends Number> iterable) {
+ double n = 0.0;
+ int n2 = 0;
+ for (Number number : iterable) {
+ n += number.doubleValue();
+ ++n2;
+ }
+ return n / n2;
+ }
+
+ public static Stream stream(T... array) {
+ return Arrays.stream(array);
+ }
+
+ public static T firstNonNull(@Nullable T t, @Nullable T t2) {
+ return t != null ? t : t2;
+ }
+
+ public static Queue trim(Queue queue, int n) {
+ for(int i = queue.size(); i > n; --i) {
+ queue.poll();
+ }
+
+ return queue;
+ }
+
+ /*public static double getStandardDeviation(Collection extends Number> values) {
+ double average = getAverage(values);
+
+ AtomicDouble variance = new AtomicDouble(0D);
+
+ values.forEach(delay -> variance.getAndAdd(Math.pow(delay.doubleValue() - average, 2D)));
+
+ return Math.sqrt(variance.get() / values.size());
+ }*/
+
+ public static double getStandardDeviation(final Collection extends Number> data) {
+ final double variance = getVariance(data);
+
+ // The standard deviation is the square root of variance. (sqrt(s^2))
+ return Math.sqrt(variance);
+ }
+
+ public static double getVariance(final Collection extends Number> data) {
+ int count = 0;
+
+ double sum = 0.0;
+ double variance = 0.0;
+
+ double average;
+
+ // Increase the sum and the count to find the average and the standard deviation
+ for (final Number number : data) {
+ sum += number.doubleValue();
+ ++count;
+ }
+
+ average = sum / count;
+
+ // Run the standard deviation formula
+ for (final Number number : data) {
+ variance += Math.pow(number.doubleValue() - average, 2.0);
+ }
+
+ return variance;
+ }
+
+ public static double getAverage(Collection extends Number> values) {
+ return values.stream()
+ .mapToDouble(Number::doubleValue)
+ .average()
+ .orElse(0D);
+ }
+
+ public static double getCPS(Collection extends Number> values) {
+ return 20.0D / getAverage(values);
+ }
+
+ public static double getCPS1000(Collection extends Number> values) {
+ return 1000D / getAverage(values);
+ }
+
+ public static double getMoveAngle(Location from, Location to) {
+ double dx = to.getX() - from.getX();
+ double dz = to.getZ() - from.getZ();
+
+ double moveAngle = Math.toDegrees(Math.atan2(dz, dx)) - 90D; // have to subtract by 90 because minecraft does it
+
+ return Math.abs(wrapAngleTo180_double(moveAngle - to.getYaw()));
+ }
+
+ public static Vector getDirection(Player player) {
+ return new Vector(-Math.sin(player.getEyeLocation().getYaw() * Math.PI / 180.0D) * 1.0D * 0.5D, 0.0D, Math.cos(player.getEyeLocation().getYaw() * Math.PI / 180.0D) * 1.0D * 0.5D);
+ //return new Vector(-Math.sin(player.getEyeLocation().getYaw() * 3.1415927F / 180.0F) * (float) 1 * 0.5F, 0, Math.cos(player.getEyeLocation().getYaw() * 3.1415927F / 180.0F) * (float) 1 * 0.5F);
+ }
+
+ public static double getDirectionShit(Location from, Location to) {
+ if (from == null || to == null) {
+ return 0.0D;
+ }
+ double difX = to.getX() - from.getX();
+ double difZ = to.getZ() - from.getZ();
+
+ return (float) ((Math.atan2(difZ, difX) * 180.0D / Math.PI) - 90.0F);
+ }
+
+ public static Vector getVectorSpeed(Location from, Location to) {
+ return new Vector(to.getX() - from.getX(), 0, to.getZ() - from.getZ());
+ }
+
+ public static double lowestAbs(Iterable extends Number> iterable) {
+ Double value = null;
+ Iterator var2 = iterable.iterator();
+
+ while(true) {
+ Number n;
+ do {
+ if (!var2.hasNext()) {
+ return (Double)firstNonNull(value, 0.0D);
+ }
+
+ n = (Number)var2.next();
+ } while(value != null && Math.abs(n.doubleValue()) >= Math.abs(value));
+
+ value = n.doubleValue();
+ }
+ }
+
+ public static Double getMinimumAngle(LocationData packetLocation, Location... array) {
+ Double value = null;
+ Location[] var3 = array;
+ int var4 = array.length;
+
+ for(int var5 = 0; var5 < var4; ++var5) {
+ Location playerLocation = var3[var5];
+ double distanceBetweenAngles360 = getDistanceBetweenAngles360((double)playerLocation.getYaw(), (double)((float)(Math.atan2(packetLocation.getZ() - playerLocation.getZ(), packetLocation.getX() - playerLocation.getX()) * 180.0D / 3.141592653589793D) - 90.0F));
+ if (value == null || value > distanceBetweenAngles360) {
+ value = distanceBetweenAngles360;
+ }
+ }
+
+ return value;
+ }
+
+ public static float averageFloat(List list) {
+ float avg = 0.0f;
+ for (float value : list) {
+ avg += value;
+ }
+ if (list.size() > 0) {
+ return avg / (float)list.size();
+ }
+ return 0.0f;
+ }
+
+ public static float averageLong(Deque list) {
+ float avg = 0;
+ for (float value : list) {
+ avg += value;
+ }
+ if (list.size() > 0) {
+ return avg / list.size();
+ }
+ return 0;
+ }
+
+ public static int getPingInTicks(long ping) {
+ return (int) Math.floor(ping / 50.);
+ }
+
+ public static int getPingToTimer(long ping) {
+ return (int) ping / 10000;
+ }
+
+
+ public static double deviation(Iterable extends Number> iterable) {
+ return Math.sqrt(deviationSquared(iterable));
+ }
+
+ public static Vector getHorizontalVector(Vector v) {
+ v.setY(0);
+ return v;
+ }
+
+ public static boolean onGround(double n) {
+ return (n % 0.015625D == 0.0D);
+ }
+
+ public static double deviationSquared(Iterable extends Number> iterable) {
+ double n = 0.0;
+ int n2 = 0;
+ for (Number number : iterable) {
+ n += number.doubleValue();
+ ++n2;
+ }
+ final double n3 = n / n2;
+ double n4 = 0.0;
+ for (Number number : iterable) {
+ n4 += Math.pow(number.doubleValue() - n3, 2.0);
+ }
+ return (n4 == 0.0) ? 0.0 : (n4 / (n2 - 1));
+ }
+
+ public static float sqrt(double var0) {
+ return (float)Math.sqrt(var0);
+ }
+
+ public static float f(List list) {
+ float n = 0.0f;
+ Iterator iterator = list.iterator();
+ while (iterator.hasNext()) {
+ n += iterator.next();
+ try {
+ if (iterator.toString() == null) {
+ return 0.0f;
+ }
+ } catch (IllegalArgumentException ex) {
+ ex.printStackTrace();
+ }
+ break;
+ }
+ try {
+ if (list.size() > 0) {
+ return n / list.size();
+ }
+ } catch (IllegalArgumentException ex2) {
+ ex2.printStackTrace();
+ }
+ return 0.0f;
+ }
+
+ public static double getHitboxSize(float yaw) {
+ var clamped = (int) Math.abs(MathUtil.clamp180(yaw)) % 90;
+
+ if (clamped > 45) {
+ clamped = 90 - clamped;
+ }
+
+ clamped /= 0.45;
+
+ val opposite = 100 - clamped;
+
+ val diagonal = HITBOX_DIAGONAL * (clamped / 100.0);
+ val normal = HITBOX_NORMAL * (opposite / 100.0);
+
+ return diagonal + normal;
+ }
+
+ public static double clamp180(double theta) {
+ theta %= 360.0;
+
+ if (theta >= 180.0) {
+ theta -= 360.0;
+ }
+
+ if (theta < -180.0) {
+ theta += 360.0;
+ }
+ return theta;
+ }
+
+ public static double getDirection(Location from, Location to) {
+ if (from == null || to == null) {
+ return 0.0D;
+ }
+ double difX = to.getX() - from.getX();
+ double difZ = to.getZ() - from.getZ();
+
+ return (float) ((Math.atan2(difZ, difX) * 180.0D / Math.PI) - 90.0F);
+ }
+
+ public static Vec3 getPositionEyes(double x, double y, double z, float eyeHeight) {
+ return new Vec3(x, y + (double)eyeHeight, z);
+ }
+
+ public Vec3 getLook(float partialTicks, PlayerData playerData) {
+ if (partialTicks == 1.0F) {
+ return getVectorForRotation(playerData.getLocation().getPitch(), playerData.getLocation().getYaw());
+ } else {
+ float f = playerData.getLastLocation().getPitch() + (playerData.getLocation().getPitch() - playerData.getLastLocation().getPitch()) * partialTicks;
+ float f1 = playerData.getLastLocation().getYaw() + (playerData.getLocation().getYaw() - playerData.getLastLocation().getYaw()) * partialTicks;
+ return getVectorForRotation(f, f1);
+ }
+ }
+
+ public static final Vec3 getVectorForRotation(float pitch, float yaw) {
+ float f = MathHelper.cos(-yaw * 0.017453292F - (float)Math.PI);
+ float f1 = MathHelper.sin(-yaw * 0.017453292F - (float)Math.PI);
+ float f2 = -MathHelper.cos(-pitch * 0.017453292F);
+ float f3 = MathHelper.sin(-pitch * 0.017453292F);
+ return new Vec3(f1 * f2, f3, f * f2);
+ }
+
+ public static AxisAlignedBB getEntityBoundingBox(double x, double y, double z) {
+ float f = 0.6F / 2.0F;
+ float f1 = 1.8F;
+ return (new AxisAlignedBB(x - (double)f, y, z - (double)f, x + (double)f, y + (double)f1, z + (double)f));
+ }
+
+ public static long gcd(final long limit, final long a, final long b) {
+ try {
+ if (b <= limit) {
+ return a;
+ }
+ } catch (IllegalArgumentException ex) {
+ ex.printStackTrace();
+ }
+ return gcd(limit, b, a % b);
+ }
+
+ public static long gcd (long a, long b){
+ if (b <= 16384) {
+ return a;
+ }
+ return gcd(b, a % b);
+ }
+
+ public static double gcd(final double limit, final double a, final double b) {
+ try {
+ if (b <= limit) {
+ return a;
+ }
+ } catch (IllegalArgumentException ex) {
+ ex.printStackTrace();
+ }
+ return gcd(limit, b, a % b);
+ }
+
+ public static long getGcd(long current, long previous) {
+ return previous <= 0b100000000000000 ? current : MathUtil.getGcd(previous, MathUtil.getDelta(current, previous));
+ }
+
+ public static T getMode(Collection collect) {
+ Map repeated = new HashMap<>();
+
+ //Sorting each value by how to repeat into a map.
+ collect.forEach(val -> {
+ int number = repeated.getOrDefault(val, 0);
+
+ repeated.put(val, number + 1);
+ });
+
+ //Calculating the largest value to the key, which would be the mode.
+ return (T) repeated.keySet().stream()
+ .map(key -> new Tuple<>(key, repeated.get(key))) //We map it into a Tuple for easier sorting.
+ .max(Comparator.comparing(tup -> tup.b(), Comparator.naturalOrder()))
+ .orElseThrow(NullPointerException::new).a();
+ }
+
+ private static String getBooleanValue3(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+
+ private static long getDelta(long alpha, long beta) {
+ return alpha % beta;
+ }
+
+ public static float[] getRotationFromPosition(CustomLocation playerLocation, CustomLocation targetLocation) {
+ double xDiff = targetLocation.getX() - playerLocation.getX();
+ double zDiff = targetLocation.getZ() - playerLocation.getZ();
+ double yDiff = targetLocation.getY() - (playerLocation.getY() + 0.12);
+ double dist = sqrt(xDiff * xDiff + zDiff * zDiff);
+ float yaw = (float) (Math.atan2(zDiff, xDiff) * 180.0 / 3.141592653589793) - 90.0f;
+ float pitch = (float) (-(Math.atan2(yDiff, dist) * 180.0 / 3.141592653589793));
+ return new float[]{yaw, pitch};
+ }
+
+ public static double pingFormula(long ping) {
+ return Math.ceil((ping + 5) / 50.0D);
+ }
+
+ public static double invSqrt(double x) {
+ double xhalf = 0.5d * x;
+ long i = Double.doubleToLongBits(x);
+ i = 0x5fe6ec85e7de30daL - (i >> 1);
+ x = Double.longBitsToDouble(i);
+ x *= (1.5d - xhalf * x * x);
+ return x;
+ }
+
+ public static double hypotNEW(double... value) {
+ double total = 0.0D;
+ double[] var3 = value;
+ int var4 = value.length;
+
+ for(int var5 = 0; var5 < var4; ++var5) {
+ double val = var3[var5];
+ total += val * val;
+ }
+
+ return Math.sqrt(total);
+ }
+
+ public static double hypot(double... value) {
+ double total = 0;
+
+ for (double val : value) {
+ total += (val * val);
+ }
+
+ return Math.sqrt(total);
+ }
+
+ public static float hypot(float... value) {
+ float total = 0;
+
+ for (float val : value) {
+ total += (val * val);
+ }
+
+ return (float) Math.sqrt(total);
+ }
+
+ public static double round(double value, int places) {
+ if (places < 0) {
+ throw new IllegalArgumentException();
+ }
+ BigDecimal bd = new BigDecimal(value);
+ bd = bd.setScale(places, RoundingMode.HALF_UP);
+ return bd.doubleValue();
+ }
+
+ public static float round(float value, int places) {
+ if (places < 0) {
+ throw new IllegalArgumentException();
+ }
+ BigDecimal bd = new BigDecimal(value);
+ bd = bd.setScale(places, RoundingMode.HALF_UP);
+ return bd.floatValue();
+ }
+
+ public static float round(float value, int places, RoundingMode mode) {
+ if (places < 0) {
+ throw new IllegalArgumentException();
+ }
+ BigDecimal bd = new BigDecimal(value);
+ bd = bd.setScale(places, mode);
+ return bd.floatValue();
+ }
+
+ public static float round(float value) {
+ BigDecimal bd = new BigDecimal(value);
+ bd = bd.setScale(0, RoundingMode.UP);
+ return bd.floatValue();
+ }
+
+
+ /*public static double hypot(double... array) {
+ return Math.sqrt(hypotSquared(array));
+ }
+
+ public static double hypotSquared(double... array) {
+ double n = 0.0D;
+ int length = array.length;
+
+ for(int i = 0; i < length; ++i) {
+ n += Math.pow(array[i], 2.0D);
+ }
+
+ return n;
+ }*/
+
+ private static String getBooleanValue4(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+
+ public static float getDistanceBetweenAngles(float angle1, float angle2) {
+ float distance = Math.abs(angle1 - angle2) % 360.0f;
+ if (distance > 180.0f) {
+ distance = 360.0f - distance;
+ }
+ return distance;
+ }
+
+
+ public static double getDistanceBetweenAngles360(double angle1, double angle2) {
+ double distance = Math.abs(angle1 % 360.0 - angle2 % 360.0);
+ distance = Math.min(360.0 - distance, distance);
+ return Math.abs(distance);
+ }
+
+ @SuppressWarnings("unused")
+ public static double[] extractFeatures(List angleSequence) {
+ List anglesDouble = toDoubleList(angleSequence);
+ List anglesDoubleDelta = calculateDelta(anglesDouble);
+
+ double featureA = stddev(anglesDouble);
+ double featureB = mean(anglesDouble);
+ double featureC = stddev(anglesDoubleDelta);
+ double featureD = mean(anglesDoubleDelta);
+
+ return new double[]{featureA, featureB, featureC, featureD};
+ }
+
+ public static double getAngleDistance(double alpha, double beta) {
+ double abs = Math.abs(alpha % 360.0D - beta % 360.0D);
+ return Math.abs(Math.min(360.0D - abs, abs));
+ }
+
+ // Get delta of a double list
+ public static List calculateDelta(List doubleList) {
+ if (doubleList.size() <= 1)
+ throw new IllegalArgumentException("The list must contain 2 or more elements in order to calculate delta");
+
+ List out = new ArrayList<>();
+ for (int i = 1; i <= doubleList.size() - 1; i++)
+ out.add(doubleList.get(i) - doubleList.get(i - 1));
+ return out;
+ }
+
+ // Convert a float list to a double list
+ public static List toDoubleList(List floatList) {
+ return floatList.stream().map(e -> (double) e).collect(Collectors.toList());
+ }
+
+ // Get mean average of a double sequence
+ public static double mean(List angles) {
+ return angles.stream().mapToDouble(e -> e).sum() / angles.size();
+ }
+
+ // Get standard deviation of a double sequence
+ public static double stddev(List angles) {
+ double mean = mean(angles);
+ double output = 0;
+ for (double angle : angles)
+ output += Math.pow(angle - mean, 2);
+ return output / angles.size();
+ }
+
+ private static String getBooleanValue1(boolean value) {
+ return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%";
+ }
+
+ // Get euclidean distance of two vector
+ public static double euclideanDistance(double[] vectorA, double[] vectorB) {
+ validateDimension("Two vectors need to have exact the same dimension", vectorA, vectorB);
+
+ double dist = 0;
+ for (int i = 0; i <= vectorA.length - 1; i++)
+ dist += Math.pow(vectorA[i] - vectorB[i], 2);
+ return Math.sqrt(dist);
+ }
+
+ // Convert a double array to a double list
+ public static List toList(double[] doubleArray) {
+ return Arrays.asList(ArrayUtils.toObject(doubleArray));
+ }
+
+ // Convert a double list to a double array
+ public static double[] toArray(List doubleList) {
+ return doubleList.stream().mapToDouble(e -> e).toArray();
+ }
+
+ // generate a double array filled with random values from 0 to 1
+ public static double[] randomArray(int length) {
+ double[] randomArray = new double[length];
+ applyFunc(randomArray, e -> e = ThreadLocalRandom.current().nextDouble());
+ return randomArray;
+ }
+
+ // apply function on a array
+ public static void applyFunc(double[] doubleArray, Function func) {
+ for (int i = 0; i <= doubleArray.length - 1; i++)
+ doubleArray[i] = func.apply(doubleArray[i]);
+ }
+
+ // add two vector together
+ public static double[] add(double[] vectorA, double[] vectorB) {
+ validateDimension("Two vectors need to have exact the same dimension", vectorA, vectorB);
+
+ double[] output = new double[vectorA.length];
+ for (int i = 0; i <= vectorA.length - 1; i++)
+ output[i] = vectorA[i] + vectorB[i];
+ return output;
+ }
+
+ // Get diff of two different vectors (subtract)
+ public static double[] subtract(double[] vectorA, double[] vectorB) {
+ validateDimension("Two vectors need to have exact the same dimension", vectorA, vectorB);
+
+ return add(vectorA, opposite(vectorB));
+ }
+
+ // get opposite numbers of elements in the vector
+ public static double[] opposite(double[] vector) {
+ return multiply(vector, -1);
+ }
+
+ // multiply all elements in the vector with a value
+ public static double[] multiply(double[] vector, double factor) {
+ double[] output = vector.clone();
+ applyFunc(output, e -> e * factor);
+ return output;
+ }
+
+ // normalize a value with feature scaling according to the given min and max
+ public static double normalize(double value, double min, double max) {
+ return (value - min) / (max - min);
+ }
+
+ // round a value with given arguments
+ public static double round(double value, int precision, RoundingMode mode) {
+ return BigDecimal.valueOf(value).round(new MathContext(precision, mode)).doubleValue();
+ }
+
+ public static double round(double value) {
+ return value - value % 1000;
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ private static void validateDimension(String message, double[]... vectors) {
+ for (int i = 0; i <= vectors.length - 1; i++)
+ if (vectors[0].length != vectors[i].length)
+ throw new IllegalArgumentException(message);
+ }
+
+ public static float wrapAngleTo180_float(float value)
+ {
+ value = value % 360.0F;
+
+ if (value >= 180.0F)
+ {
+ value -= 360.0F;
+ }
+
+ if (value < -180.0F)
+ {
+ value += 360.0F;
+ }
+
+ return value;
+ }
+
+ public static double wrapAngleTo180_double(double value) {
+ value %= 360D;
+
+ if (value >= 180D)
+ value -= 360D;
+
+ if (value < -180D)
+ value += 360D;
+
+ return value;
+ }
+
+ public static boolean isUsingOptifine(Location current, Location previous) {
+ val yawChange = Math.abs(current.getYaw() - previous.getYaw());
+ val pitchChange = Math.abs(current.getPitch() - previous.getPitch());
+
+ val yawWrapped = MathUtil.wrapAngleTo180_float(yawChange);
+ val pitchWrapped = MathUtil.wrapAngleTo180_float(pitchChange);
+
+ return pitchWrapped < 0.01 || yawWrapped < 0.015;
+ }
+
+ public static double positiveSmaller(Number n, Number n2) {
+ return Math.abs(n.doubleValue()) < Math.abs(n2.doubleValue()) ? n.doubleValue() : n2.doubleValue();
+ }
+
+ public static double sigmoid(double x) {
+ return 1.0 / (1.0 + Math.exp(-x));
+ }
+
+ public static final Random RANDOM = new Random();
+
+ public static double getDistanceToGround(Player p) {
+ Location loc = p.getLocation().clone();
+ double y = loc.getBlockY();
+ double distance = 0.0;
+ for (double i = y; i >= 0.0; --i) {
+ loc.setY(i);
+ if (loc.getBlock().getType().isSolid()) {
+ break;
+ }
+ ++distance;
+ }
+ return distance;
+ }
+
+}
diff --git a/src/main/java/me/liwk/karhu/util/MouseFilter.java b/src/main/java/me/liwk/karhu/util/MouseFilter.java
new file mode 100644
index 0000000..cf69f05
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/MouseFilter.java
@@ -0,0 +1,33 @@
+package me.liwk.karhu.util;
+
+public class MouseFilter {
+
+ private float field_76336_a;
+ private float field_76334_b;
+ private float field_76335_c;
+
+ /**
+ * Smooths mouse input
+ */
+ public float smooth(float p_76333_1_, float p_76333_2_)
+ {
+ this.field_76336_a += p_76333_1_;
+ p_76333_1_ = (this.field_76336_a - this.field_76334_b) * p_76333_2_;
+ this.field_76335_c += (p_76333_1_ - this.field_76335_c) * 0.5F;
+
+ if (p_76333_1_ > 0.0F && p_76333_1_ > this.field_76335_c || p_76333_1_ < 0.0F && p_76333_1_ < this.field_76335_c)
+ {
+ p_76333_1_ = this.field_76335_c;
+ }
+
+ this.field_76334_b += p_76333_1_;
+ return p_76333_1_;
+ }
+
+ public void reset() {
+ this.field_76336_a = 0.0F;
+ this.field_76334_b = 0.0F;
+ this.field_76335_c = 0.0F;
+ }
+}
+
diff --git a/src/main/java/me/liwk/karhu/util/MovementUtils.java b/src/main/java/me/liwk/karhu/util/MovementUtils.java
new file mode 100644
index 0000000..dd48843
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/MovementUtils.java
@@ -0,0 +1,54 @@
+package me.liwk.karhu.util;
+
+import org.bukkit.Location;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
+import org.bukkit.util.Vector;
+
+public class MovementUtils {
+
+ public static double getHorizontalDistanceSpeed(Location to, Location from, Player p) {
+ double x = Math.abs(to.getX()) - Math.abs(from.getX());
+ double z = Math.abs(to.getZ()) - Math.abs(from.getZ());
+ return Math.sqrt(x * x + z * z);
+ }
+
+ public static int getPotionEffectLevel(Player player, PotionEffectType pet) {
+ for (PotionEffect pe : player.getActivePotionEffects()) {
+ if (pe.getType().getName().equals(pet.getName())) {
+ return pe.getAmplifier() + 1;
+ }
+ }
+ return 0;
+ }
+ public static double offset(Vector from, Vector to) {
+ from.setY(0);
+ to.setY(0);
+
+ return to.subtract(from).length();
+ }
+
+ public static int getDepthStriderLevel(Player player) {
+ if (player.getInventory().getBoots() != null && hasEnchantment(player.getInventory().getBoots(), Enchantment.getByName("DEPTH_STRIDER"))) {
+ return player.getInventory().getBoots().getEnchantments().get(Enchantment.getByName("DEPTH_STRIDER"));
+ }
+
+ return 0;
+ }
+
+ public static int getSoulSpeedLevel(Player player) {
+ if (player.getInventory().getBoots() != null && hasEnchantment(player.getInventory().getBoots(), Enchantment.getByName("SOUL_SPEED"))) {
+ return player.getInventory().getBoots().getEnchantments().get(Enchantment.getByName("SOUL_SPEED"));
+ }
+
+ return 0;
+ }
+
+ public static boolean hasEnchantment(ItemStack item, Enchantment enchantment) {
+ return item.getEnchantments().keySet().contains(enchantment);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/me/liwk/karhu/util/MovingObjectPosition.java b/src/main/java/me/liwk/karhu/util/MovingObjectPosition.java
new file mode 100644
index 0000000..972e206
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/MovingObjectPosition.java
@@ -0,0 +1,61 @@
+package me.liwk.karhu.util;
+
+import org.bukkit.entity.Entity;
+
+public class MovingObjectPosition
+{
+ private BlockPos blockPos;
+
+ /** What type of ray trace hit was this? 0 = block, 1 = entity */
+ public MovingObjectPosition.MovingObjectType typeOfHit;
+ public EnumFacing sideHit;
+
+ /** The vector position of the hit */
+ public Vec3 hitVec;
+
+ /** The hit entity */
+ public Entity entityHit;
+
+ public MovingObjectPosition(Vec3 hitVecIn, EnumFacing facing, BlockPos blockPosIn)
+ {
+ this(MovingObjectPosition.MovingObjectType.BLOCK, hitVecIn, facing, blockPosIn);
+ }
+
+ public MovingObjectPosition(Vec3 p_i45552_1_, EnumFacing facing)
+ {
+ this(MovingObjectPosition.MovingObjectType.BLOCK, p_i45552_1_, facing, BlockPos.ORIGIN);
+ }
+
+ public MovingObjectPosition(MovingObjectPosition.MovingObjectType typeOfHitIn, Vec3 hitVecIn, EnumFacing sideHitIn, BlockPos blockPosIn)
+ {
+ this.typeOfHit = typeOfHitIn;
+ this.blockPos = blockPosIn;
+ this.sideHit = sideHitIn;
+ this.hitVec = new Vec3(hitVecIn.xCoord, hitVecIn.yCoord, hitVecIn.zCoord);
+ }
+
+ public MovingObjectPosition(Entity entityHitIn, Vec3 hitVecIn)
+ {
+ this.typeOfHit = MovingObjectPosition.MovingObjectType.ENTITY;
+ this.entityHit = entityHitIn;
+ this.hitVec = hitVecIn;
+ }
+
+ public BlockPos getBlockPos()
+ {
+ return this.blockPos;
+ }
+
+ public String toString()
+ {
+ return "HitResult{type=" + this.typeOfHit + ", blockpos=" + this.blockPos + ", f=" + this.sideHit + ", pos=" + this.hitVec + ", entity=" + this.entityHit + '}';
+ }
+
+ public static enum MovingObjectType
+ {
+ MISS,
+ BLOCK,
+ ENTITY;
+ }
+}
+
diff --git a/src/main/java/me/liwk/karhu/util/Objects.java b/src/main/java/me/liwk/karhu/util/Objects.java
new file mode 100644
index 0000000..718dd33
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/Objects.java
@@ -0,0 +1,174 @@
+package me.liwk.karhu.util;
+
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.base.Preconditions;
+
+import javax.annotation.CheckReturnValue;
+import javax.annotation.Nullable;
+import java.util.Arrays;
+
+@GwtCompatible
+public final class Objects {
+ private Objects() {
+ }
+
+ @CheckReturnValue
+ public static boolean equal(@Nullable Object a, @Nullable Object b) {
+ return a == b || a != null && a.equals(b);
+ }
+
+ public static int hashCode(@Nullable Object... objects) {
+ return Arrays.hashCode(objects);
+ }
+
+ public static ToStringHelper toStringHelper(Object self) {
+ return new ToStringHelper(simpleName(self.getClass()));
+ }
+
+ public static ToStringHelper toStringHelper(Class> clazz) {
+ return new ToStringHelper(simpleName(clazz));
+ }
+
+ public static ToStringHelper toStringHelper(String className) {
+ return new ToStringHelper(className);
+ }
+
+ private static String simpleName(Class> clazz) {
+ String name = clazz.getName();
+ name = name.replaceAll("\\$[0-9]+", "\\$");
+ int start = name.lastIndexOf(36);
+ if (start == -1) {
+ start = name.lastIndexOf(46);
+ }
+
+ return name.substring(start + 1);
+ }
+
+ public static T firstNonNull(@Nullable T first, @Nullable T second) {
+ return first != null ? first : Preconditions.checkNotNull(second);
+ }
+
+ public static final class ToStringHelper {
+ private final String className;
+ private ToStringHelper.ValueHolder holderHead;
+ private ToStringHelper.ValueHolder holderTail;
+ private boolean omitNullValues;
+
+ private ToStringHelper(String className) {
+ this.holderHead = new ToStringHelper.ValueHolder();
+ this.holderTail = this.holderHead;
+ this.omitNullValues = false;
+ this.className = (String)Preconditions.checkNotNull(className);
+ }
+
+ public ToStringHelper omitNullValues() {
+ this.omitNullValues = true;
+ return this;
+ }
+
+ public ToStringHelper add(String name, @Nullable Object value) {
+ return this.addHolder(name, value);
+ }
+
+ public ToStringHelper add(String name, boolean value) {
+ return this.addHolder(name, String.valueOf(value));
+ }
+
+ public ToStringHelper add(String name, char value) {
+ return this.addHolder(name, String.valueOf(value));
+ }
+
+ public ToStringHelper add(String name, double value) {
+ return this.addHolder(name, String.valueOf(value));
+ }
+
+ public ToStringHelper add(String name, float value) {
+ return this.addHolder(name, String.valueOf(value));
+ }
+
+ public ToStringHelper add(String name, int value) {
+ return this.addHolder(name, String.valueOf(value));
+ }
+
+ public ToStringHelper add(String name, long value) {
+ return this.addHolder(name, String.valueOf(value));
+ }
+
+ public ToStringHelper addValue(@Nullable Object value) {
+ return this.addHolder(value);
+ }
+
+ public ToStringHelper addValue(boolean value) {
+ return this.addHolder(String.valueOf(value));
+ }
+
+ public ToStringHelper addValue(char value) {
+ return this.addHolder(String.valueOf(value));
+ }
+
+ public ToStringHelper addValue(double value) {
+ return this.addHolder(String.valueOf(value));
+ }
+
+ public ToStringHelper addValue(float value) {
+ return this.addHolder(String.valueOf(value));
+ }
+
+ public ToStringHelper addValue(int value) {
+ return this.addHolder(String.valueOf(value));
+ }
+
+ public ToStringHelper addValue(long value) {
+ return this.addHolder(String.valueOf(value));
+ }
+
+ public String toString() {
+ boolean omitNullValuesSnapshot = this.omitNullValues;
+ String nextSeparator = "";
+ StringBuilder builder = (new StringBuilder(32)).append(this.className).append('{');
+
+ for(ToStringHelper.ValueHolder valueHolder = this.holderHead.next; valueHolder != null; valueHolder = valueHolder.next) {
+ if (!omitNullValuesSnapshot || valueHolder.value != null) {
+ builder.append(nextSeparator);
+ nextSeparator = ", ";
+ if (valueHolder.name != null) {
+ builder.append(valueHolder.name).append('=');
+ }
+
+ builder.append(valueHolder.value);
+ }
+ }
+
+ return builder.append('}').toString();
+ }
+
+ private ToStringHelper.ValueHolder addHolder() {
+ ToStringHelper.ValueHolder valueHolder = new ToStringHelper.ValueHolder();
+ this.holderTail = this.holderTail.next = valueHolder;
+ return valueHolder;
+ }
+
+ private ToStringHelper addHolder(@Nullable Object value) {
+ ToStringHelper.ValueHolder valueHolder = this.addHolder();
+ valueHolder.value = value;
+ return this;
+ }
+
+ private ToStringHelper addHolder(String name, @Nullable Object value) {
+ ToStringHelper.ValueHolder valueHolder = this.addHolder();
+ valueHolder.value = value;
+ valueHolder.name = (String)Preconditions.checkNotNull(name);
+ return this;
+ }
+
+ private static final class ValueHolder {
+ String name;
+ Object value;
+ ToStringHelper.ValueHolder next;
+
+ private ValueHolder() {
+ }
+ }
+ }
+}
+
diff --git a/src/main/java/me/liwk/karhu/util/Observable.java b/src/main/java/me/liwk/karhu/util/Observable.java
new file mode 100644
index 0000000..dc15e74
--- /dev/null
+++ b/src/main/java/me/liwk/karhu/util/Observable.java
@@ -0,0 +1,48 @@
+package me.liwk.karhu.util;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public final class Observable {
+
+
+
+ Set> observers = Collections.newSetFromMap(new ConcurrentHashMap, Boolean>());
+ //private final Set> observers = Sets.new();
+ private T value;
+
+ public Observable(final T initValue) {
+ this.value = initValue;
+ }
+
+ public T get() {
+ return value;
+ }
+
+ public void set(final T value) {
+
+ final T oldValue = this.value;
+
+ this.value = value;
+
+ observers.forEach((it) -> it.handle(oldValue, value));
+ }
+
+
+ public ChangeObserver observe(final ChangeObserver onChange) {
+ observers.add(onChange);
+ return onChange;
+ }
+
+ public void unobserve(final ChangeObserver onChange) {
+ observers.remove(onChange);
+ }
+
+ @FunctionalInterface
+ public interface ChangeObserver