From ffb7d97b1d00ad77b8a7d92dccb479e6d0309f22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maurice=20Eisenbl=C3=A4tter?= <code@ingrim4.me>
Date: Sun, 6 Oct 2024 15:38:11 +0200
Subject: [PATCH 1/5] feat: add support for 1.21

---
 pom.xml                                       |  6 +--
 .../bukkit/plugin/packets/MapDataPacket.java  | 29 +++++++++--
 .../bukkit/plugin/renderer/FakeEntity.java    |  2 +-
 .../bukkit/plugin/renderer/FakeImage.java     | 29 ++++++-----
 .../bukkit/plugin/utils/Internals.java        | 40 +++++++--------
 .../bukkit/plugin/utils/WrappedMapId.java     | 49 +++++++++++++++++++
 6 files changed, 113 insertions(+), 42 deletions(-)
 create mode 100644 src/main/java/io/josemmo/bukkit/plugin/utils/WrappedMapId.java

diff --git a/pom.xml b/pom.xml
index 892a65b..1c52ed2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,9 +103,9 @@
 
         <!-- https://jitpack.io/com/github/TownyAdvanced/towny/ -->
         <dependency>
-            <groupId>com.github.TownyAdvanced</groupId>
-            <artifactId>towny</artifactId>
-            <version>0.100.2.11</version>
+            <groupId>com.github.TownyAdvanced.towny</groupId>
+			<artifactId>towny</artifactId>
+			<version>0.100.1.17</version>
             <scope>provided</scope>
         </dependency>
 
diff --git a/src/main/java/io/josemmo/bukkit/plugin/packets/MapDataPacket.java b/src/main/java/io/josemmo/bukkit/plugin/packets/MapDataPacket.java
index 734e16f..91b2985 100644
--- a/src/main/java/io/josemmo/bukkit/plugin/packets/MapDataPacket.java
+++ b/src/main/java/io/josemmo/bukkit/plugin/packets/MapDataPacket.java
@@ -1,11 +1,18 @@
 package io.josemmo.bukkit.plugin.packets;
 
+import java.lang.reflect.ParameterizedType;
+import java.util.Optional;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
 import com.comphenix.protocol.PacketType;
 import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.injector.StructureCache;
 import com.comphenix.protocol.reflect.StructureModifier;
+
 import io.josemmo.bukkit.plugin.utils.Internals;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import io.josemmo.bukkit.plugin.utils.WrappedMapId;
 
 public class MapDataPacket extends PacketContainer {
     private static final int LOCKED_INDEX;
@@ -26,12 +33,28 @@ public MapDataPacket() {
             // Create modifier for map data instance
             Class<?> mapDataType = getModifier().getField(4).getType();
             Object mapDataInstance = getModifier().read(4);
+
+            // MapPatch is wrapped inside Optional since 1.20.5+
+            if (mapDataInstance instanceof Optional) {
+            	ParameterizedType genericType = (ParameterizedType) getModifier().getField(4).getGenericType();
+            	mapDataType = (Class<?>) genericType.getActualTypeArguments()[0];
+
+            	// Create new MapPatch instance as ProtocolLib won't initialize Optionals for us
+            	mapDataInstance = StructureCache.newInstance(mapDataType);
+            	getModifier().write(4, Optional.of(mapDataInstance));
+
+            	// Set decorations Optional to an empty Optional (ProtocolLib initializes Optionals wrong)
+            	getModifier().write(3, Optional.empty());
+            }
+
             mapDataModifier = new StructureModifier<>(mapDataType).withTarget(mapDataInstance);
         }
     }
 
     public @NotNull MapDataPacket setId(int id) {
-        getIntegers().write(0, id);
+    	if (!WrappedMapId.trySetMapId(this, id)) {
+            getIntegers().write(0, id);
+    	}
         return this;
     }
 
diff --git a/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeEntity.java b/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeEntity.java
index a503653..67fbcde 100644
--- a/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeEntity.java
+++ b/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeEntity.java
@@ -88,7 +88,7 @@ protected static void tryToSendPacket(@NotNull Player player, @NotNull PacketCon
             if (NETWORK_MANAGER_INJECTOR == null) { // Use single-threaded packet sending if reflection failed
                 CONNECTION.sendServerPacket(player, packet);
             } else { // Use non-blocking packet sending if available (faster, the expected case)
-                NETWORK_MANAGER_INJECTOR.getInjector(player).sendClientboundPacket(packet, null, false);
+                NETWORK_MANAGER_INJECTOR.getInjector(player).sendClientboundPacket(packet.getHandle(), null, false);
             }
         } catch (IllegalStateException e) {
             // Server is shutting down and cannot send the packet, ignore
diff --git a/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java b/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java
index 8fe4adb..1048df6 100644
--- a/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java
+++ b/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java
@@ -444,19 +444,24 @@ public void spawn(@NotNull Player player) {
      * @param player Player instance
      */
     private void spawnOnceLoaded(@NotNull Player player) {
-        String playerName = player.getName();
-        observingPlayers.add(player);
-
-        // Prepare packets to send
-        List<PacketContainer> packets = new ArrayList<>();
-        for (FakeItemFrame frame : frames) {
-            packets.add(frame.getSpawnPacket());
-            packets.addAll(frame.getRenderPackets(player, 0));
-            LOGGER.fine("Spawned FakeItemFrame#" + frame.getId() + " for Player#" + playerName);
-        }
+    	try {
+            String playerName = player.getName();
+            observingPlayers.add(player);
+
+            // Prepare packets to send
+            List<PacketContainer> packets = new ArrayList<>();
+            for (FakeItemFrame frame : frames) {
+                packets.add(frame.getSpawnPacket());
+                packets.addAll(frame.getRenderPackets(player, 0));
+                LOGGER.fine("Spawned FakeItemFrame#" + frame.getId() + " for Player#" + playerName);
+            }
 
-        // Send packets
-        tryToSendPackets(player, packets);
+            // Send packets
+            tryToSendPackets(player, packets);
+		} catch (Exception e) {
+            // We should log errors and don't just let the default thread exception handler silence them
+			e.printStackTrace();
+		}
     }
 
     /**
diff --git a/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java b/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java
index 4bcb955..5ba6803 100644
--- a/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java
+++ b/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java
@@ -1,14 +1,18 @@
 package io.josemmo.bukkit.plugin.utils;
 
-import com.mojang.brigadier.CommandDispatcher;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
 import org.bukkit.Bukkit;
 import org.bukkit.Server;
 import org.bukkit.command.CommandMap;
 import org.bukkit.command.CommandSender;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
+
+import com.comphenix.protocol.reflect.FuzzyReflection;
+import com.comphenix.protocol.utility.MinecraftReflection;
+import com.mojang.brigadier.CommandDispatcher;
 
 public class Internals {
     public static final float MINECRAFT_VERSION;
@@ -28,28 +32,18 @@ public class Internals {
             Class<?> obcClass = obcInstance.getClass();
 
             // Get "net.minecraft.server.MinecraftServer" references
-            Object nmsInstance = obcClass.getDeclaredMethod("getServer").invoke(obcInstance);
-            Class<?> nmsClass = nmsInstance.getClass().getSuperclass();
+            Object nmsServerInstance = obcClass.getDeclaredMethod("getServer").invoke(obcInstance);
 
-            if (MINECRAFT_VERSION >= 20.5) {
-                // Get "net.minecraft.commands.Commands" references
-                Object nmsCommandsInstance = nmsClass.getDeclaredMethod("getCommands").invoke(nmsInstance);
-                Class<?> nmsCommandsClass = nmsCommandsInstance.getClass();
+            // Get "net.minecraft.server.CommandDispatcher" references
+            Class<?> nmsDispatcherClass = MinecraftReflection.getMinecraftClass("commands.CommandDispatcher");
+            Object nmsDispatcherInstance = FuzzyReflection.fromObject(nmsServerInstance, true)
+            	.getMethodByReturnTypeAndParameters("getDispatcher", nmsDispatcherClass)
+            	.invoke(nmsServerInstance);
 
-                // Get "com.mojang.brigadier.CommandDispatcher" instance
-                Field nmsDispatcherField = nmsCommandsClass.getDeclaredField("dispatcher");
-                nmsDispatcherField.setAccessible(true);
-                DISPATCHER = (CommandDispatcher<?>) nmsDispatcherField.get(nmsCommandsInstance);
-            } else {
-                // Get "net.minecraft.server.CommandDispatcher" references
-                Object nmsDispatcherInstance = nmsClass.getDeclaredField("vanillaCommandDispatcher").get(nmsInstance);
-                Class<?> nmsDispatcherClass = nmsDispatcherInstance.getClass();
-
-                // Get "com.mojang.brigadier.CommandDispatcher" instance
-                Method getDispatcherMethod = nmsDispatcherClass.getDeclaredMethod("a");
-                getDispatcherMethod.setAccessible(true);
-                DISPATCHER = (CommandDispatcher<?>) getDispatcherMethod.invoke(nmsDispatcherInstance);
-            }
+            // Get "com.mojang.brigadier.CommandDispatcher" instance
+            DISPATCHER = (CommandDispatcher<?>) FuzzyReflection.fromObject(nmsDispatcherInstance, true)
+            	.getMethodByReturnTypeAndParameters("getDispatcher", CommandDispatcher.class)
+            	.invoke(nmsDispatcherInstance);
 
             // Get command map instance
             Field commandMapField = obcClass.getDeclaredField("commandMap");
diff --git a/src/main/java/io/josemmo/bukkit/plugin/utils/WrappedMapId.java b/src/main/java/io/josemmo/bukkit/plugin/utils/WrappedMapId.java
new file mode 100644
index 0000000..889f570
--- /dev/null
+++ b/src/main/java/io/josemmo/bukkit/plugin/utils/WrappedMapId.java
@@ -0,0 +1,49 @@
+package io.josemmo.bukkit.plugin.utils;
+
+import java.lang.reflect.Constructor;
+import java.util.function.IntFunction;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.reflect.ExactReflection;
+import com.comphenix.protocol.reflect.StructureModifier;
+import com.comphenix.protocol.utility.MinecraftReflection;
+
+public class WrappedMapId {
+
+	private static final @Nullable Class<?> MAP_ID_CLASS = MinecraftReflection.getNullableNMS("world.level.saveddata.maps.MapId");
+	private static final @Nullable IntFunction<?> MAP_ID_CONSTRUTOR = getMapIdConstructor();
+
+	private static IntFunction<Object> getMapIdConstructor() {
+		if (MAP_ID_CLASS == null) {
+			return null;
+		}
+
+		Constructor<?> constructor = ExactReflection.fromClass(MAP_ID_CLASS, true)
+			.findConstructor(int.class);
+		if (constructor == null) {
+			return null;
+		}
+
+		return (mapId) -> {
+			try {
+				return constructor.newInstance(mapId);
+			} catch (Exception e) {
+				throw new RuntimeException("An error occurred while creating a MapId", e);
+			}
+		};
+	}
+
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public static boolean trySetMapId(PacketContainer packetContainer, int mapId) {
+		if (MAP_ID_CLASS == null || MAP_ID_CONSTRUTOR == null) {
+			return false;
+		}
+
+		StructureModifier mapIds = packetContainer.getSpecificModifier(MAP_ID_CLASS);
+		mapIds.write(0, MAP_ID_CONSTRUTOR.apply(mapId));
+
+		return true;
+	}
+}

From f32221398687749cf3c2e8835fcd6eddcf45c3b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maurice=20Eisenbl=C3=A4tter?= <code@ingrim4.me>
Date: Sun, 6 Oct 2024 17:37:05 +0200
Subject: [PATCH 2/5] fix: add support for vanilla class mappings

---
 src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java b/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java
index 5ba6803..a3f1df0 100644
--- a/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java
+++ b/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java
@@ -35,7 +35,10 @@ public class Internals {
             Object nmsServerInstance = obcClass.getDeclaredMethod("getServer").invoke(obcInstance);
 
             // Get "net.minecraft.server.CommandDispatcher" references
-            Class<?> nmsDispatcherClass = MinecraftReflection.getMinecraftClass("commands.CommandDispatcher");
+            Class<?> nmsDispatcherClass = MinecraftReflection.getMinecraftClass(
+            	/*spigot (1.16.x)*/"CommandDispatcher",
+            	/*spigot*/"commands.CommandDispatcher",
+            	/*mojang/paper*/"commands.Commands");
             Object nmsDispatcherInstance = FuzzyReflection.fromObject(nmsServerInstance, true)
             	.getMethodByReturnTypeAndParameters("getDispatcher", nmsDispatcherClass)
             	.invoke(nmsServerInstance);

From 25d5a33d86ec51f91346dc4d6c4eb71902b3cc30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Miguel=20Moreno?= <josemmo@pm.me>
Date: Sun, 13 Oct 2024 11:22:31 +0200
Subject: [PATCH 3/5] Simplified MapDataPacket

- Updated MapDataPacket class
- Removed WrappedMapId class
---
 .../bukkit/plugin/packets/MapDataPacket.java  | 62 ++++++++++---------
 .../bukkit/plugin/utils/WrappedMapId.java     | 49 ---------------
 2 files changed, 34 insertions(+), 77 deletions(-)
 delete mode 100644 src/main/java/io/josemmo/bukkit/plugin/utils/WrappedMapId.java

diff --git a/src/main/java/io/josemmo/bukkit/plugin/packets/MapDataPacket.java b/src/main/java/io/josemmo/bukkit/plugin/packets/MapDataPacket.java
index 91b2985..1fb3496 100644
--- a/src/main/java/io/josemmo/bukkit/plugin/packets/MapDataPacket.java
+++ b/src/main/java/io/josemmo/bukkit/plugin/packets/MapDataPacket.java
@@ -1,25 +1,31 @@
 package io.josemmo.bukkit.plugin.packets;
 
-import java.lang.reflect.ParameterizedType;
-import java.util.Optional;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
 import com.comphenix.protocol.PacketType;
 import com.comphenix.protocol.events.PacketContainer;
 import com.comphenix.protocol.injector.StructureCache;
+import com.comphenix.protocol.reflect.ExactReflection;
 import com.comphenix.protocol.reflect.StructureModifier;
-
+import com.comphenix.protocol.utility.MinecraftReflection;
 import io.josemmo.bukkit.plugin.utils.Internals;
-import io.josemmo.bukkit.plugin.utils.WrappedMapId;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.ParameterizedType;
+import java.util.Optional;
 
 public class MapDataPacket extends PacketContainer {
     private static final int LOCKED_INDEX;
+    private static final @Nullable Constructor<?> MAP_ID_CONSTRUCTOR;
     private @Nullable StructureModifier<?> mapDataModifier;
 
     static {
         LOCKED_INDEX = (Internals.MINECRAFT_VERSION < 17) ? 1 : 0;
+        if (Internals.MINECRAFT_VERSION < 20.5) {
+            MAP_ID_CONSTRUCTOR = null;
+        } else {
+            Class<?> mapIdClass = MinecraftReflection.getNullableNMS("world.level.saveddata.maps.MapId");
+            MAP_ID_CONSTRUCTOR = ExactReflection.fromClass(mapIdClass, true).findConstructor(int.class);
+        }
     }
 
     public MapDataPacket() {
@@ -27,34 +33,34 @@ public MapDataPacket() {
         getModifier().writeDefaults();
 
         if (Internals.MINECRAFT_VERSION < 17) {
-            // Disable tracking position
-            getBooleans().write(0, false);
-        } else {
-            // Create modifier for map data instance
+            getBooleans().write(0, false); // Disable tracking position
+        } else if (Internals.MINECRAFT_VERSION < 20.5) {
             Class<?> mapDataType = getModifier().getField(4).getType();
             Object mapDataInstance = getModifier().read(4);
-
-            // MapPatch is wrapped inside Optional since 1.20.5+
-            if (mapDataInstance instanceof Optional) {
-            	ParameterizedType genericType = (ParameterizedType) getModifier().getField(4).getGenericType();
-            	mapDataType = (Class<?>) genericType.getActualTypeArguments()[0];
-
-            	// Create new MapPatch instance as ProtocolLib won't initialize Optionals for us
-            	mapDataInstance = StructureCache.newInstance(mapDataType);
-            	getModifier().write(4, Optional.of(mapDataInstance));
-
-            	// Set decorations Optional to an empty Optional (ProtocolLib initializes Optionals wrong)
-            	getModifier().write(3, Optional.empty());
-            }
-
+            mapDataModifier = new StructureModifier<>(mapDataType).withTarget(mapDataInstance);
+        } else {
+            ParameterizedType genericType = (ParameterizedType) getModifier().getField(4).getGenericType();
+            Class<?> mapDataType = (Class<?>) genericType.getActualTypeArguments()[0];
+            Object mapDataInstance = StructureCache.newInstance(mapDataType);
+            getModifier().write(3, Optional.empty());
+            getModifier().write(4, Optional.of(mapDataInstance));
             mapDataModifier = new StructureModifier<>(mapDataType).withTarget(mapDataInstance);
         }
     }
 
+    @SuppressWarnings({"unchecked", "rawtypes"})
     public @NotNull MapDataPacket setId(int id) {
-    	if (!WrappedMapId.trySetMapId(this, id)) {
+        if (MAP_ID_CONSTRUCTOR == null) {
             getIntegers().write(0, id);
-    	}
+        } else {
+            try {
+                Class<?> mapIdClass = MAP_ID_CONSTRUCTOR.getDeclaringClass();
+                Object mapIdInstance = MAP_ID_CONSTRUCTOR.newInstance(id);
+                ((StructureModifier) getSpecificModifier(mapIdClass)).write(0, mapIdInstance);
+            } catch (Exception e) {
+                throw new RuntimeException("Failed to instantiate MapId for map #" + id);
+            }
+        }
         return this;
     }
 
diff --git a/src/main/java/io/josemmo/bukkit/plugin/utils/WrappedMapId.java b/src/main/java/io/josemmo/bukkit/plugin/utils/WrappedMapId.java
deleted file mode 100644
index 889f570..0000000
--- a/src/main/java/io/josemmo/bukkit/plugin/utils/WrappedMapId.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package io.josemmo.bukkit.plugin.utils;
-
-import java.lang.reflect.Constructor;
-import java.util.function.IntFunction;
-
-import org.jetbrains.annotations.Nullable;
-
-import com.comphenix.protocol.events.PacketContainer;
-import com.comphenix.protocol.reflect.ExactReflection;
-import com.comphenix.protocol.reflect.StructureModifier;
-import com.comphenix.protocol.utility.MinecraftReflection;
-
-public class WrappedMapId {
-
-	private static final @Nullable Class<?> MAP_ID_CLASS = MinecraftReflection.getNullableNMS("world.level.saveddata.maps.MapId");
-	private static final @Nullable IntFunction<?> MAP_ID_CONSTRUTOR = getMapIdConstructor();
-
-	private static IntFunction<Object> getMapIdConstructor() {
-		if (MAP_ID_CLASS == null) {
-			return null;
-		}
-
-		Constructor<?> constructor = ExactReflection.fromClass(MAP_ID_CLASS, true)
-			.findConstructor(int.class);
-		if (constructor == null) {
-			return null;
-		}
-
-		return (mapId) -> {
-			try {
-				return constructor.newInstance(mapId);
-			} catch (Exception e) {
-				throw new RuntimeException("An error occurred while creating a MapId", e);
-			}
-		};
-	}
-
-	@SuppressWarnings({ "unchecked", "rawtypes" })
-	public static boolean trySetMapId(PacketContainer packetContainer, int mapId) {
-		if (MAP_ID_CLASS == null || MAP_ID_CONSTRUTOR == null) {
-			return false;
-		}
-
-		StructureModifier mapIds = packetContainer.getSpecificModifier(MAP_ID_CLASS);
-		mapIds.write(0, MAP_ID_CONSTRUTOR.apply(mapId));
-
-		return true;
-	}
-}

From c6ff5bb8775b4924af540f6852983458b85aa50c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Miguel=20Moreno?= <josemmo@pm.me>
Date: Sun, 13 Oct 2024 11:28:08 +0200
Subject: [PATCH 4/5] Minor code style changes

- Updated FakeEntity and FakeImage classes
- Updated Internals class
---
 .../bukkit/plugin/renderer/FakeEntity.java    |  6 +++-
 .../bukkit/plugin/renderer/FakeImage.java     | 29 ++++++++-----------
 .../bukkit/plugin/utils/Internals.java        |  9 +++---
 3 files changed, 21 insertions(+), 23 deletions(-)

diff --git a/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeEntity.java b/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeEntity.java
index 67fbcde..f39783b 100644
--- a/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeEntity.java
+++ b/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeEntity.java
@@ -88,7 +88,11 @@ protected static void tryToSendPacket(@NotNull Player player, @NotNull PacketCon
             if (NETWORK_MANAGER_INJECTOR == null) { // Use single-threaded packet sending if reflection failed
                 CONNECTION.sendServerPacket(player, packet);
             } else { // Use non-blocking packet sending if available (faster, the expected case)
-                NETWORK_MANAGER_INJECTOR.getInjector(player).sendClientboundPacket(packet.getHandle(), null, false);
+                NETWORK_MANAGER_INJECTOR.getInjector(player).sendClientboundPacket(
+                    packet.getHandle(),
+                    null,
+                    false
+                );
             }
         } catch (IllegalStateException e) {
             // Server is shutting down and cannot send the packet, ignore
diff --git a/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java b/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java
index 1048df6..8fe4adb 100644
--- a/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java
+++ b/src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java
@@ -444,24 +444,19 @@ public void spawn(@NotNull Player player) {
      * @param player Player instance
      */
     private void spawnOnceLoaded(@NotNull Player player) {
-    	try {
-            String playerName = player.getName();
-            observingPlayers.add(player);
-
-            // Prepare packets to send
-            List<PacketContainer> packets = new ArrayList<>();
-            for (FakeItemFrame frame : frames) {
-                packets.add(frame.getSpawnPacket());
-                packets.addAll(frame.getRenderPackets(player, 0));
-                LOGGER.fine("Spawned FakeItemFrame#" + frame.getId() + " for Player#" + playerName);
-            }
+        String playerName = player.getName();
+        observingPlayers.add(player);
+
+        // Prepare packets to send
+        List<PacketContainer> packets = new ArrayList<>();
+        for (FakeItemFrame frame : frames) {
+            packets.add(frame.getSpawnPacket());
+            packets.addAll(frame.getRenderPackets(player, 0));
+            LOGGER.fine("Spawned FakeItemFrame#" + frame.getId() + " for Player#" + playerName);
+        }
 
-            // Send packets
-            tryToSendPackets(player, packets);
-		} catch (Exception e) {
-            // We should log errors and don't just let the default thread exception handler silence them
-			e.printStackTrace();
-		}
+        // Send packets
+        tryToSendPackets(player, packets);
     }
 
     /**
diff --git a/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java b/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java
index a3f1df0..bb13820 100644
--- a/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java
+++ b/src/main/java/io/josemmo/bukkit/plugin/utils/Internals.java
@@ -2,14 +2,12 @@
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
-
 import org.bukkit.Bukkit;
 import org.bukkit.Server;
 import org.bukkit.command.CommandMap;
 import org.bukkit.command.CommandSender;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
-
 import com.comphenix.protocol.reflect.FuzzyReflection;
 import com.comphenix.protocol.utility.MinecraftReflection;
 import com.mojang.brigadier.CommandDispatcher;
@@ -36,9 +34,10 @@ public class Internals {
 
             // Get "net.minecraft.server.CommandDispatcher" references
             Class<?> nmsDispatcherClass = MinecraftReflection.getMinecraftClass(
-            	/*spigot (1.16.x)*/"CommandDispatcher",
-            	/*spigot*/"commands.CommandDispatcher",
-            	/*mojang/paper*/"commands.Commands");
+            	"CommandDispatcher", // Spigot <1.17
+            	"commands.CommandDispatcher", // Spigot >=1.17
+            	"commands.Commands" // PaperMC
+            );
             Object nmsDispatcherInstance = FuzzyReflection.fromObject(nmsServerInstance, true)
             	.getMethodByReturnTypeAndParameters("getDispatcher", nmsDispatcherClass)
             	.invoke(nmsServerInstance);

From 955c7c2a1f982c9a76104e0d48b0a52183688bd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Miguel=20Moreno?= <josemmo@pm.me>
Date: Sun, 13 Oct 2024 11:38:39 +0200
Subject: [PATCH 5/5] Upgraded dependencies

---
 pom.xml | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/pom.xml b/pom.xml
index 1c52ed2..ca26e6d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,7 +42,7 @@
         <dependency>
             <groupId>org.spigotmc</groupId>
             <artifactId>spigot-api</artifactId>
-            <version>1.20.6-R0.1-SNAPSHOT</version>
+            <version>1.21.1-R0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
 
@@ -66,7 +66,7 @@
         <dependency>
             <groupId>org.bstats</groupId>
             <artifactId>bstats-bukkit</artifactId>
-            <version>3.0.2</version>
+            <version>3.1.0</version>
         </dependency>
 
         <!-- https://central.sonatype.com/artifact/net.luckperms/api -->
@@ -103,17 +103,17 @@
 
         <!-- https://jitpack.io/com/github/TownyAdvanced/towny/ -->
         <dependency>
-            <groupId>com.github.TownyAdvanced.towny</groupId>
-			<artifactId>towny</artifactId>
-			<version>0.100.1.17</version>
+            <groupId>com.github.TownyAdvanced.Towny</groupId>
+            <artifactId>towny</artifactId>
+            <version>0.100.4.4</version>
             <scope>provided</scope>
         </dependency>
 
         <!-- https://jitpack.io/com/github/Angeschossen/LandsAPI/ -->
         <dependency>
-            <groupId>com.github.angeschossen</groupId>
+            <groupId>com.github.Angeschossen</groupId>
             <artifactId>LandsAPI</artifactId>
-            <version>7.1.12</version>
+            <version>7.9.17</version>
             <scope>provided</scope>
         </dependency>
 
@@ -121,7 +121,7 @@
         <dependency>
             <groupId>org.jetbrains</groupId>
             <artifactId>annotations</artifactId>
-            <version>24.1.0</version>
+            <version>26.0.0</version>
             <scope>provided</scope>
         </dependency>
     </dependencies>