From 11ee9016fb8a27631482d19a912a4618a824d915 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:15:40 +0100 Subject: [PATCH 01/50] Ajout Folia Supported dans plugin.yml --- main/src/main/resources/plugin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/main/src/main/resources/plugin.yml b/main/src/main/resources/plugin.yml index 4dfec5114..2048f740b 100644 --- a/main/src/main/resources/plugin.yml +++ b/main/src/main/resources/plugin.yml @@ -5,6 +5,7 @@ version: ${project.version} (build ${BUILD_NUMBER}) main: net.citizensnpcs.Citizens website: https://www.citizensnpcs.co api-version: "1.13" +folia-supported: true libraries: - ch.ethz.globis.phtree:phtree:2.8.2 - org.joml:joml:1.10.8 From 33aa586efb24a502dfb8ab3d1c80025648c10bfc Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:18:50 +0100 Subject: [PATCH 02/50] =?UTF-8?q?Folia=20ne=20permet=20pas=20de=20supprime?= =?UTF-8?q?r=20les=20NPcs=20quand=20il=20s'arr=C3=AAte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/src/main/java/net/citizensnpcs/Citizens.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main/src/main/java/net/citizensnpcs/Citizens.java b/main/src/main/java/net/citizensnpcs/Citizens.java index ac2d2139c..487142430 100644 --- a/main/src/main/java/net/citizensnpcs/Citizens.java +++ b/main/src/main/java/net/citizensnpcs/Citizens.java @@ -185,6 +185,9 @@ private void despawnNPCs(boolean save) { registry.saveToStore(); } } + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + if (!this.isEnabled()) return; + } registry.despawnNPCs(DespawnReason.RELOAD); } } From 69127b39d65055d830657582c7266dda05e84908 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:19:31 +0100 Subject: [PATCH 03/50] Replace scheduler dans Citizens.java --- main/src/main/java/net/citizensnpcs/Citizens.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/Citizens.java b/main/src/main/java/net/citizensnpcs/Citizens.java index 487142430..66922c416 100644 --- a/main/src/main/java/net/citizensnpcs/Citizens.java +++ b/main/src/main/java/net/citizensnpcs/Citizens.java @@ -447,7 +447,7 @@ public void onEnable() { // Setup NPCs after all plugins have been enabled (allows for multiworld // support and for NPCs to properly register external settings) - if (getServer().getScheduler().scheduleSyncDelayedTask(this, new CitizensLoadTask(), 1) == -1) { + if (CitizensAPI.getScheduler().runTaskLater(() -> new CitizensLoadTask().run(), 1) == null) { Messaging.severeTr(Messages.LOAD_TASK_NOT_SCHEDULED); Bukkit.getPluginManager().disablePlugin(this); } @@ -515,7 +515,7 @@ public void removeNamedNPCRegistry(String name) { } private void scheduleSaveTask(int delay) { - Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new CitizensSaveTask(), delay, delay); + CitizensAPI.getScheduler().runTaskTimer(() -> new CitizensSaveTask().run(), delay, delay); } @Override From 67ee2c44a7cdbf73c7c6273f188860a8eeb4e257 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:22:09 +0100 Subject: [PATCH 04/50] Replace scheduler dans EventListen.java --- .../java/net/citizensnpcs/EventListen.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/EventListen.java b/main/src/main/java/net/citizensnpcs/EventListen.java index 73fc083b5..269dd603a 100644 --- a/main/src/main/java/net/citizensnpcs/EventListen.java +++ b/main/src/main/java/net/citizensnpcs/EventListen.java @@ -265,7 +265,8 @@ void loadNPCs(ChunkEvent event) { if (event instanceof Cancellable) { runnable.run(); } else { - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, runnable); + final org.bukkit.Chunk chunk = event.getChunk(); + CitizensAPI.getScheduler().runRegionTask(chunk.getWorld(), chunk.getX(), chunk.getZ(), runnable); } } @@ -420,7 +421,7 @@ public void onEntityDeath(EntityDeathEvent event) { return; int deathAnimationTicks = event.getEntity() instanceof LivingEntity ? 20 : 2; - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { + CitizensAPI.getScheduler().runRegionTaskLater(location, () -> { if (!npc.isSpawned() && npc.getOwningRegistry().getByUniqueId(npc.getUniqueId()) == npc) { npc.spawn(location, SpawnReason.TIMED_RESPAWN); } @@ -536,7 +537,7 @@ public void onNPCLinkToPlayer(NPCLinkToPlayerEvent event) { } if (npc.data().has(NPC.Metadata.HOLOGRAM_RENDERER)) { HologramRenderer hr = npc.data().get(NPC.Metadata.HOLOGRAM_RENDERER); - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> hr.onSeenByPlayer(npc, event.getPlayer()), 2); + CitizensAPI.getScheduler().runEntityTaskLater(event.getPlayer(), () -> hr.onSeenByPlayer(npc, event.getPlayer()), 2); } } @@ -558,12 +559,12 @@ private void onNPCPlayerLinkToPlayer(NPCLinkToPlayerEvent event) { if (!sendTabRemove || !event.getNPC().shouldRemoveFromTabList()) { NMS.sendRotationPacket(tracker, ImmutableList.of(event.getPlayer()), null, null, NMS.getHeadYaw(tracker)); if (resetYaw) { - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, + CitizensAPI.getScheduler().runEntityTask(tracker, () -> PlayerAnimation.ARM_SWING.play((Player) tracker, event.getPlayer())); } return; } - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { + CitizensAPI.getScheduler().runRegionTaskLater(tracker.getLocation(), () -> { if (!tracker.isValid() || !event.getPlayer().isValid()) return; @@ -612,7 +613,7 @@ public void onPlayerChangedWorld(PlayerChangedWorldEvent event) { if (plugin.getNPCRegistry().getNPC(event.getPlayer()) == null) return; - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { + CitizensAPI.getScheduler().runEntityTaskLater(event.getPlayer(), () -> { NMS.replaceTracker(event.getPlayer()); NMS.removeFromServerPlayerList(event.getPlayer()); }, 1); @@ -675,7 +676,7 @@ public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { if (SUPPORT_STOP_USE_ITEM) { try { PlayerAnimation.STOP_USE_ITEM.play(player); - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, + CitizensAPI.getScheduler().runEntityTask(player, () -> PlayerAnimation.STOP_USE_ITEM.play(player)); } catch (UnsupportedOperationException e) { SUPPORT_STOP_USE_ITEM = false; @@ -744,9 +745,9 @@ public void onPlayerTeleport(PlayerTeleportEvent event) { if (event.getCause() == TeleportCause.PLUGIN && npc != null && !npc.data().has("citizens-force-teleporting") && Setting.PLAYER_TELEPORT_DELAY.asTicks() > 0) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { + CitizensAPI.getScheduler().runEntityTaskLater(event.getPlayer(), () -> { npc.data().set("citizens-force-teleporting", true); - event.getPlayer().teleport(event.getTo()); + SpigotUtil.teleportAsync(event.getPlayer(), event.getTo()); npc.data().remove("citizens-force-teleporting"); }, Setting.PLAYER_TELEPORT_DELAY.asTicks()); } @@ -789,7 +790,7 @@ public void onProjectileHit(ProjectileHitEvent event) { if (!(event.getEntity() instanceof FishHook)) return; NMS.removeHookIfNecessary((FishHook) event.getEntity()); - new BukkitRunnable() { + new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { int n = 0; @Override @@ -800,7 +801,7 @@ public void run() { } NMS.removeHookIfNecessary((FishHook) event.getEntity()); } - }.runTaskTimer(plugin, 0, 1); + }.runEntityTaskTimer(plugin, event.getEntity(), null, 0, 1); } @EventHandler @@ -932,11 +933,11 @@ private void registerMoveEvent(Class clazz) { if (!npcMoveEvent.isCancelled()) { final Location eventTo = npcMoveEvent.getTo(); if (!to.equals(eventTo)) { - Bukkit.getScheduler().runTaskLater(plugin, () -> entity.teleport(eventTo), 1L); + CitizensAPI.getScheduler().runEntityTaskLater(entity, () -> SpigotUtil.teleportAsync(entity, eventTo), 1L); } } else { final Location eventFrom = npcMoveEvent.getFrom(); - Bukkit.getScheduler().runTaskLater(plugin, () -> entity.teleport(eventFrom), 1L); + CitizensAPI.getScheduler().runEntityTaskLater(entity, () -> SpigotUtil.teleportAsync(entity, eventFrom), 1L); } } catch (Throwable ex) { ex.printStackTrace(); @@ -1044,7 +1045,8 @@ private void unloadNPCs(ChunkEvent event, List toDespawn) { } if (loadChunk) { Messaging.idebug(() -> Joiner.on(' ').join("Loading chunk in 10 ticks due to forced chunk load at", coord)); - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { + final org.bukkit.Chunk chunk = event.getChunk(); + CitizensAPI.getScheduler().runRegionTaskLater(chunk.getWorld(), chunk.getX(), chunk.getZ(), () -> { if (!event.getChunk().isLoaded()) { event.getChunk().load(); } From ecb0be3d32eff4f647a59c770b013b02cb7f4835 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:23:30 +0100 Subject: [PATCH 05/50] Replace scheduler in Metrics.java --- main/src/main/java/net/citizensnpcs/Metrics.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/Metrics.java b/main/src/main/java/net/citizensnpcs/Metrics.java index 143373b80..e1457b4dc 100644 --- a/main/src/main/java/net/citizensnpcs/Metrics.java +++ b/main/src/main/java/net/citizensnpcs/Metrics.java @@ -29,6 +29,7 @@ import javax.net.ssl.HttpsURLConnection; +import net.citizensnpcs.api.CitizensAPI; import org.bukkit.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; @@ -88,7 +89,7 @@ public Metrics(JavaPlugin plugin, int serviceId) { boolean logSentData = config.getBoolean("logSentData", false); boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); metricsBase = new MetricsBase("bukkit", serverUUID, serviceId, enabled, this::appendPlatformData, - this::appendServiceData, submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), + this::appendServiceData, submitDataTask -> CitizensAPI.getScheduler().runTask(submitDataTask), plugin::isEnabled, (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), message -> this.plugin.getLogger().log(Level.INFO, message), logErrors, logSentData, logResponseStatusText); From 26a5979e290abce00f6fa4a17b2eb42c90f3df26 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:25:02 +0100 Subject: [PATCH 06/50] Replace scheduler dans NPCCommands.java --- .../net/citizensnpcs/commands/NPCCommands.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index 3d4597945..f4447d87c 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -898,7 +898,7 @@ else if (!EntityControllers.controllerExistsForType(type)) } if (temporaryDuration != null) { NPC temp = npc; - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runEntityTaskLater(temp.getEntity(), () -> { if (temporaryRegistry.getByUniqueId(temp.getUniqueId()) == temp) { temp.destroy(); } @@ -3175,14 +3175,14 @@ public void skin(CommandContext args, CommandSender sender, NPC npc, @Flag("url" return; } else if (url != null || file != null) { Messaging.sendTr(sender, Messages.FETCHING_SKIN, url == null ? file : url); - Bukkit.getScheduler().runTaskAsynchronously(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runTaskAsynchronously(() -> { try { JSONObject data = null; if (file != null) { File skinsFolder = new File(CitizensAPI.getDataFolder(), "skins"); File skin = new File(skinsFolder, Placeholders.replace(file, sender, npc)); if (!skin.exists() || !skin.isFile() || skin.isHidden() || !isInDirectory(skin, skinsFolder)) { - Bukkit.getScheduler().runTask(CitizensAPI.getPlugin(), + CitizensAPI.getScheduler().runTask( () -> Messaging.sendErrorTr(sender, Messages.INVALID_SKIN_FILE, file)); return; } @@ -3197,7 +3197,7 @@ public void skin(CommandContext args, CommandSender sender, NPC npc, @Flag("url" String textureEncoded = (String) texture.get("value"); String signature = (String) texture.get("signature"); - Bukkit.getScheduler().runTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runEntityTask(npc.getEntity(), () -> { try { trait.setSkinPersistent(uuid, signature, textureEncoded); Messaging.sendTr(sender, Messages.SKIN_URL_SET, npc.getName(), url == null ? file : url); @@ -3209,7 +3209,7 @@ public void skin(CommandContext args, CommandSender sender, NPC npc, @Flag("url" if (Messaging.isDebugging()) { t.printStackTrace(); } - Bukkit.getScheduler().runTask(CitizensAPI.getPlugin(), () -> Messaging.sendErrorTr(sender, + CitizensAPI.getScheduler().runTask(() -> Messaging.sendErrorTr(sender, Messages.ERROR_SETTING_SKIN_URL, url == null ? file : url)); } }); @@ -3588,7 +3588,7 @@ public void tp(CommandContext args, Player player, NPC npc) { to = to.clone().add(to.getDirection().setY(0)); to.setDirection(to.getDirection().multiply(-1)).setPitch(0); } - player.teleport(to, TeleportCause.COMMAND); + SpigotUtil.teleportAsync(player, to, TeleportCause.COMMAND); Messaging.sendTr(player, Messages.TELEPORTED_TO_NPC, npc.getName()); } @@ -3689,7 +3689,7 @@ public void tpto(CommandContext args, CommandSender sender, NPC npc) throws Comm throw new CommandException(Messages.FROM_ENTITY_NOT_FOUND); if (to == null) throw new CommandException(Messages.TPTO_ENTITY_NOT_FOUND); - from.teleport(to); + SpigotUtil.teleportAsync(from, to.getLocation()); Messaging.sendTr(sender, Messages.TPTO_SUCCESS); } From 98730011f7fcc6998dfd9feeef9b9cc6655807ff Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:25:46 +0100 Subject: [PATCH 07/50] Replace scheduler dans CitizensNPC.java --- main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index db5d97c40..f34964441 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -431,12 +431,12 @@ public void accept(Runnable cancel) { postSpawn.accept(() -> { }); } else { - new BukkitRunnable() { + new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { @Override public void run() { postSpawn.accept(this::cancel); } - }.runTaskTimer(CitizensAPI.getPlugin(), 0, 1); + }.runEntityTaskTimer(CitizensAPI.getPlugin(), getEntity(), null, 0, 1); } return true; } From d49c2451f43ebe59fef707b1aec085abb7185687 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:27:50 +0100 Subject: [PATCH 08/50] Replace scheduler dans Skin.java --- main/src/main/java/net/citizensnpcs/npc/skin/Skin.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java b/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java index 61821466d..0d06bf97d 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java @@ -37,7 +37,7 @@ public class Skin { private boolean hasFetched; private volatile boolean isValid = true; private final Map pending = new WeakHashMap<>(15); - private BukkitTask retryTask; + private net.citizensnpcs.api.util.schedulers.SchedulerTask retryTask; private volatile SkinProperty skinData; private volatile UUID skinId; private final String skinName; @@ -125,7 +125,7 @@ public void applyAndRespawn(SkinnableEntity entity) { if (!npc.isSpawned()) return; - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runEntityTask(npc.getEntity(), () -> { npc.despawn(DespawnReason.PENDING_RESPAWN); npc.spawn(npc.getStoredLocation(), SpawnReason.RESPAWN); }); @@ -158,7 +158,7 @@ private void fetch() { fetchRetries++; long delay = Setting.NPC_SKIN_RETRY_DELAY.asTicks(); - retryTask = Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), (Runnable) this::fetch, + retryTask = CitizensAPI.getScheduler().runTaskLater(this::fetch, delay); Messaging.idebug(() -> "Retrying skin fetch for '" + skinName + "' in " + delay + " ticks."); @@ -200,8 +200,8 @@ private void fetchForced() { } fetchRetries++; int delay = Setting.NPC_SKIN_RETRY_DELAY.asTicks(); - retryTask = Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), - (Runnable) this::fetchForced, delay); + retryTask = CitizensAPI.getScheduler().runTaskLater( + this::fetchForced, delay); Messaging.idebug(() -> "Retrying skin fetch for '" + skinName + "' in " + delay + " ticks."); break; From 303106c2b8f90519d4f0265c883ec205bf47ad4f Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:28:22 +0100 Subject: [PATCH 09/50] Replace scheduler dans SkinPacketTracker.java --- .../main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java b/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java index 5ad1508b6..b10b9538d 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java @@ -64,7 +64,7 @@ public void updateViewer(Player player) { skin.apply(entity); if (entity.getBukkitEntity() instanceof Player && NMS.sendTabListAdd(player, (Player) entity.getBukkitEntity()) && entity.getNPC().shouldRemoveFromTabList()) { - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), + CitizensAPI.getScheduler().runEntityTaskLater(player, () -> NMS.sendTabListRemove(player, (Player) entity.getBukkitEntity()), Setting.TABLIST_REMOVE_PACKET_DELAY.asTicks()); } From e6be69cd0b8cc8d4451d122eecd07ed4b6275d48 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:29:17 +0100 Subject: [PATCH 10/50] Replace scheduler dans SkinUpdateTracker.java --- .../java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java index e1ca0bd40..3b8c53bc4 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java @@ -288,7 +288,7 @@ public void updatePlayer(Player player, long delay, boolean reset) { if (player.hasMetadata("NPC")) return; - new BukkitRunnable() { + new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { @Override public void run() { List visible = getNearbyNPCs(player, reset, false); @@ -300,11 +300,11 @@ public void run() { skinnable.getSkinTracker().updateViewer(player); } } - }.runTaskLater(CitizensAPI.getPlugin(), delay); + }.runEntityTaskLater(CitizensAPI.getPlugin(), player, null, delay); } // update players when the NPC navigates into their field of view - private class NPCNavigationTracker extends BukkitRunnable { + private class NPCNavigationTracker extends net.citizensnpcs.api.util.schedulers.SchedulerRunnable { @Override public void run() { if (navigating.isEmpty() || playerTrackers.isEmpty()) @@ -332,7 +332,7 @@ public void run() { // Updates players. Repeating task used to schedule updates without // causing excessive scheduling. - private static class NPCNavigationUpdater extends BukkitRunnable { + private static class NPCNavigationUpdater extends net.citizensnpcs.api.util.schedulers.SchedulerRunnable { Queue queue = new ArrayDeque<>(20); @Override From ce49c2c0f91e152d9a64a4a37ebec4dbe8a85b3f Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:30:02 +0100 Subject: [PATCH 11/50] Replace scheduler dans ProfileFetchThread.java --- .../net/citizensnpcs/npc/skin/profile/ProfileFetchThread.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileFetchThread.java b/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileFetchThread.java index cad95945b..336255feb 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileFetchThread.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileFetchThread.java @@ -230,7 +230,7 @@ public void run() { } private static void addHandler(ProfileRequest request, Consumer handler) { - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> request.addHandler(handler), 1); + CitizensAPI.getScheduler().runTaskLater(() -> request.addHandler(handler), 1); } @Nullable @@ -264,6 +264,6 @@ private static boolean isTooManyRequests(Throwable e) { } private static void sendResult(Consumer handler, ProfileRequest request) { - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> handler.accept(request), 1); + CitizensAPI.getScheduler().runTaskLater(() -> handler.accept(request), 1); } } From 20f9d9160ed2592c63ba4738b73f8417a7a9cfee Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:30:48 +0100 Subject: [PATCH 12/50] Replace scheduler dans ProfileFetcher.java --- .../net/citizensnpcs/npc/skin/profile/ProfileFetcher.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileFetcher.java b/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileFetcher.java index e21d8b719..b71965521 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileFetcher.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileFetcher.java @@ -48,7 +48,7 @@ private static void initThread() { THREAD_TASK.cancel(); } PROFILE_THREAD = new ProfileFetchThread(); - THREAD_TASK = Bukkit.getScheduler().runTaskTimerAsynchronously(CitizensAPI.getPlugin(), PROFILE_THREAD, 21, 20); + THREAD_TASK = CitizensAPI.getScheduler().runTaskTimerAsynchronously(PROFILE_THREAD, 21, 20); } /** @@ -66,5 +66,5 @@ public static void shutdown() { } private static ProfileFetchThread PROFILE_THREAD; - private static BukkitTask THREAD_TASK; + private static net.citizensnpcs.api.util.schedulers.SchedulerTask THREAD_TASK; } From 9802e107680bdeb678ba7c7e350abc001f02fb04 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:31:17 +0100 Subject: [PATCH 13/50] Replace scheduler dans ProfileRequest.java --- .../java/net/citizensnpcs/npc/skin/profile/ProfileRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileRequest.java b/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileRequest.java index 39b45f707..2d6873976 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileRequest.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/profile/ProfileRequest.java @@ -132,7 +132,7 @@ public int hashCode() { void setResult(@Nullable GameProfile profile, ProfileFetchResult result) { if (!CitizensAPI.hasImplementation()) return; - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runTask(() -> { ProfileRequest.this.profile = profile; ProfileRequest.this.result = result; From 462606557724dfef24bb2a1ac1d9840495fcb22c Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:32:02 +0100 Subject: [PATCH 14/50] Replace scheduler dans CommandTrait.java --- .../main/java/net/citizensnpcs/trait/CommandTrait.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java b/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java index f56a85d7b..9ccd7d6d1 100644 --- a/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java @@ -373,7 +373,7 @@ private void runCommand(Player player, Hand hand, NPCCommand command) { if (temporaryPermissionsDuration <= 0) { attachment.remove(); } else { - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), + CitizensAPI.getScheduler().runEntityTask(player, () -> attachment.remove()); } return; @@ -384,14 +384,14 @@ private void runCommand(Player player, Hand hand, NPCCommand command) { if (command.delay <= 0) { runnable.run(); } else { - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), runnable, command.delay); + CitizensAPI.getScheduler().runEntityTaskLater(player, runnable, command.delay); } } }; - if (Bukkit.isPrimaryThread()) { + if (CitizensAPI.getScheduler().isOnOwnerThread(player)) { task.run(); } else { - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), task); + CitizensAPI.getScheduler().runEntityTask(player, task); } } From c0f6622eea09558b4df9a4f206fb5614cf5a4151 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:32:32 +0100 Subject: [PATCH 15/50] Replace scheduler dans PacketNPC.java --- main/src/main/java/net/citizensnpcs/trait/PacketNPC.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/PacketNPC.java b/main/src/main/java/net/citizensnpcs/trait/PacketNPC.java index 8d174770f..e61b42ada 100644 --- a/main/src/main/java/net/citizensnpcs/trait/PacketNPC.java +++ b/main/src/main/java/net/citizensnpcs/trait/PacketNPC.java @@ -35,7 +35,7 @@ public EntityPacketTracker getPacketTracker() { public void onRemove(RemoveReason reason) { if (reason == RemoveReason.REMOVAL) { npc.despawn(DespawnReason.PENDING_RESPAWN); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runRegionTask(npc.getStoredLocation(), () -> { if (npc.getStoredLocation() != null) { npc.spawn(npc.getStoredLocation(), SpawnReason.RESPAWN); } From b418e492816bb4e92df18c4d02649a86b189a7ce Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:34:05 +0100 Subject: [PATCH 16/50] Scordboard not supported on folia --- .../net/citizensnpcs/trait/PausePathfindingTrait.java | 10 +++++----- .../java/net/citizensnpcs/trait/ScoreboardTrait.java | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/PausePathfindingTrait.java b/main/src/main/java/net/citizensnpcs/trait/PausePathfindingTrait.java index 4470cfbe6..a3d07f09e 100644 --- a/main/src/main/java/net/citizensnpcs/trait/PausePathfindingTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/PausePathfindingTrait.java @@ -22,7 +22,7 @@ public class PausePathfindingTrait extends Trait { @Persist("rightclick") private boolean rightclick; private int t; - private int unpauseTaskId = -1; + private net.citizensnpcs.api.util.schedulers.SchedulerTask unpauseTaskId = null; public PausePathfindingTrait() { super("pausepathfinding"); @@ -49,12 +49,12 @@ public void onInteract(NPCRightClickEvent event) { } private void pause() { - if (unpauseTaskId != -1) { - Bukkit.getScheduler().cancelTask(unpauseTaskId); + if (unpauseTaskId != null) { + unpauseTaskId.cancel(); } npc.getNavigator().cancelNavigation(); npc.getNavigator().setPaused(true); - unpauseTaskId = Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + unpauseTaskId = CitizensAPI.getScheduler().runEntityTaskLater(npc.getEntity(), () -> { NMS.setPitch(npc.getEntity(), 0); npc.getNavigator().setPaused(false); }, pauseTicks <= 0 ? 20 : pauseTicks); @@ -68,7 +68,7 @@ public boolean pauseOnRightClick() { @Override public void run() { if (lockoutDuration > t++ || playerRange == -1 || !npc.isSpawned() - || unpauseTaskId == -1 && !npc.getNavigator().isNavigating()) + || unpauseTaskId == null && !npc.getNavigator().isNavigating()) return; if (CitizensAPI.getLocationLookup() diff --git a/main/src/main/java/net/citizensnpcs/trait/ScoreboardTrait.java b/main/src/main/java/net/citizensnpcs/trait/ScoreboardTrait.java index cf895da87..acc31b783 100644 --- a/main/src/main/java/net/citizensnpcs/trait/ScoreboardTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/ScoreboardTrait.java @@ -66,6 +66,7 @@ private void clearClientTeams(Team team) { } public void createTeam(String entityName) { + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) return; // Not Supported on Folia String teamName = Util.getTeamName(npc.getUniqueId()); npc.data().set(NPC.Metadata.SCOREBOARD_FAKE_TEAM_NAME, teamName); Scoreboard scoreboard = Util.getDummyScoreboard(); @@ -99,6 +100,7 @@ public void load(DataKey key) { @Override public void onDespawn(DespawnReason reason) { + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) return; // Not Supported on Folia previousGlowingColor = null; String name = lastName; String teamName = npc.data().get(NPC.Metadata.SCOREBOARD_FAKE_TEAM_NAME, ""); @@ -134,7 +136,7 @@ public void onDespawn(DespawnReason reason) { if (reason == DespawnReason.REMOVAL || reason == DespawnReason.RELOAD) { cleanup.run(); } else { - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), cleanup, + CitizensAPI.getScheduler().runEntityTaskLater(npc.getEntity(), cleanup, reason == DespawnReason.DEATH && npc.getEntity() instanceof LivingEntity ? 20 : 2); } } From a8dec9309e10b1b3ca10890a15849ad66f5dff82 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:34:43 +0100 Subject: [PATCH 17/50] Replace scheduler dans ShopTrait.java --- main/src/main/java/net/citizensnpcs/trait/ShopTrait.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java index e5a3cae40..b81636777 100644 --- a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java @@ -1221,7 +1221,7 @@ public void changePage(int newPage) { currentPage = newPage; NPCShopPage page = shop.pages.get(currentPage); if (page.title != null && !page.title.isEmpty()) { - Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), + CitizensAPI.getScheduler().runEntityTaskLater(player, () -> ctx.setTitle(Placeholders.replace(page.title, player)), 1); } for (int i = 0; i < ctx.getInventory().getSize(); i++) { From 6ef64b9deb660e6d855d7070330fac6655dee7f7 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:35:25 +0100 Subject: [PATCH 18/50] Replace scheduler dans OpenShopAction.java --- .../main/java/net/citizensnpcs/trait/shop/OpenShopAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/shop/OpenShopAction.java b/main/src/main/java/net/citizensnpcs/trait/shop/OpenShopAction.java index 025cb9bc0..3f064b183 100644 --- a/main/src/main/java/net/citizensnpcs/trait/shop/OpenShopAction.java +++ b/main/src/main/java/net/citizensnpcs/trait/shop/OpenShopAction.java @@ -56,7 +56,7 @@ public Transaction take(NPCShopStorage storage, Entity entity, InventoryMultiple // TODO: support hierarchical shops? would need to call InventoryMenu#transition somehow return Transaction.create(() -> shop.canView(player), () -> { player.closeInventory(); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> shop.display(player)); + CitizensAPI.getScheduler().runEntityTask(player, () -> shop.display(player)); }, () -> { // TODO: closeInventory()? transitionBack()? }); From 1d77e3ec313128420c429a2a92b510704c5c9310 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:37:48 +0100 Subject: [PATCH 19/50] Replace scheduler dans GuidedWaypointProvider.java --- .../trait/waypoint/GuidedWaypointProvider.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/waypoint/GuidedWaypointProvider.java b/main/src/main/java/net/citizensnpcs/trait/waypoint/GuidedWaypointProvider.java index f296c89f1..4b64cbdc5 100644 --- a/main/src/main/java/net/citizensnpcs/trait/waypoint/GuidedWaypointProvider.java +++ b/main/src/main/java/net/citizensnpcs/trait/waypoint/GuidedWaypointProvider.java @@ -145,7 +145,7 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { return; if (event.getMessage().equalsIgnoreCase("triggers")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runEntityTask(player, () -> { conversation = TriggerEditPrompt.start(player, this); conversation.addConversationAbandonedListener(e -> { setPaused(false); @@ -155,10 +155,10 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { }); } else if (event.getMessage().equalsIgnoreCase("toggle path")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), this::togglePath); + CitizensAPI.getScheduler().runTask(this::togglePath); } else if (event.getMessage().equalsIgnoreCase("clear")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runTask(() -> { destinations.clear(); guides.clear(); if (showPath) { @@ -170,7 +170,7 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { double d = Double.parseDouble(event.getMessage().replace("distance ", "").trim()); if (d <= 0) return; - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runTask(() -> { distance = (float) d; Messaging.sendTr(sender, Messages.GUIDED_WAYPOINT_EDITOR_DISTANCE_SET, d); }); From 29659f401685d4899a77f4e29ad8991e0769268f Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:38:39 +0100 Subject: [PATCH 20/50] Replace scheduler dans LinearWaypointProvider.java --- .../trait/waypoint/LinearWaypointProvider.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java b/main/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java index 4e7291871..610234269 100644 --- a/main/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java +++ b/main/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java @@ -359,7 +359,7 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { String message = event.getMessage(); if (message.equalsIgnoreCase("triggers")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runEntityTask(event.getPlayer(), () -> { conversation = TriggerEditPrompt.start(player, LinearWaypointEditor.this); conversation.addConversationAbandonedListener(e -> { setPaused(false); @@ -369,20 +369,20 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { }); } else if (message.equalsIgnoreCase("clear")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), this::clearWaypoints); + CitizensAPI.getScheduler().runEntityTask(event.getPlayer(), this::clearWaypoints); } else if (message.equalsIgnoreCase("toggle path") || message.equalsIgnoreCase("markers")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), this::togglePath); + CitizensAPI.getScheduler().runEntityTask(event.getPlayer(), this::togglePath); } else if (message.equalsIgnoreCase("cycle")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runEntityTask(event.getPlayer(), () -> { cycle = !cycle; Messaging.sendTr(event.getPlayer(), cycle ? Messages.LINEAR_WAYPOINT_EDITOR_CYCLE_SET : Messages.LINEAR_WAYPOINT_EDITOR_CYCLE_UNSET); }); } else if (message.equalsIgnoreCase("here")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), + CitizensAPI.getScheduler().runEntityTask(event.getPlayer(), () -> addWaypoint(player.getLocation())); } } From 0a19dac0f3977c2566a18a34d283ad798055ebb4 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:40:16 +0100 Subject: [PATCH 21/50] Replace scheduler dans WanderWaypointProvider.java --- .../trait/waypoint/WanderWaypointProvider.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/waypoint/WanderWaypointProvider.java b/main/src/main/java/net/citizensnpcs/trait/waypoint/WanderWaypointProvider.java index 98a087714..cab5ce2db 100644 --- a/main/src/main/java/net/citizensnpcs/trait/waypoint/WanderWaypointProvider.java +++ b/main/src/main/java/net/citizensnpcs/trait/waypoint/WanderWaypointProvider.java @@ -110,17 +110,19 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { recalculateTree(); } catch (NumberFormatException ex) { } - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), + CitizensAPI.getScheduler().runTask( () -> Messaging.sendTr(sender, Messages.WANDER_WAYPOINTS_RANGE_SET, xrange, yrange)); } else if (message.startsWith("regions")) { event.setCancelled(true); editingRegions = !editingRegions; - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runTask(() -> { if (editingRegions) { for (Location regionCentre : regionCentres) { - Entity entity = markers.createMarker(regionCentre, regionCentre); - entity.setMetadata("wandermarker", - new FixedMetadataValue(CitizensAPI.getPlugin(), regionCentre)); + CitizensAPI.getScheduler().runRegionTask(regionCentre, () -> { + Entity entity = markers.createMarker(regionCentre, regionCentre); + entity.setMetadata("wandermarker", + new FixedMetadataValue(CitizensAPI.getPlugin(), regionCentre)); + }); } Messaging.sendTr(sender, Messages.WANDER_WAYPOINTS_REGION_EDITING_START); } else { @@ -131,11 +133,11 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { } else if (message.startsWith("delay")) { event.setCancelled(true); setDelay(Util.parseTicks(message.split(" ")[1])); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), + CitizensAPI.getScheduler().runTask( () -> Messaging.sendTr(sender, Messages.WANDER_WAYPOINTS_DELAY_SET, delay)); } else if (message.startsWith("worldguardregion")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runTask(() -> { Object region = null; String regionId = message.replace("worldguardregion", "").trim(); if (regionId.isEmpty()) { @@ -159,7 +161,7 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { }); } else if (message.startsWith("pathfind")) { event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runTask(() -> { pathfind = !pathfind; if (currentGoal != null) { currentGoal.setPathfind(pathfind); From 170a618f0e19d68c8be32607b90cf30734e65069 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:40:49 +0100 Subject: [PATCH 22/50] Replace scheduler dans Waypoint.java --- .../src/main/java/net/citizensnpcs/trait/waypoint/Waypoint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/waypoint/Waypoint.java b/main/src/main/java/net/citizensnpcs/trait/waypoint/Waypoint.java index a50f33e14..4ae14ad41 100644 --- a/main/src/main/java/net/citizensnpcs/trait/waypoint/Waypoint.java +++ b/main/src/main/java/net/citizensnpcs/trait/waypoint/Waypoint.java @@ -114,7 +114,7 @@ private void runTriggers(NPC npc, int start) { continue; } int newStart = i + 1; - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> runTriggers(npc, newStart), + CitizensAPI.getScheduler().runEntityTaskLater(npc.getEntity(), () -> runTriggers(npc, newStart), delay); break; } From 740fa9693f059237fa4d60ad37046b92e742cc27 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:41:18 +0100 Subject: [PATCH 23/50] Replace scheduler dans DelayTrigger.java --- .../net/citizensnpcs/trait/waypoint/triggers/DelayTrigger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/waypoint/triggers/DelayTrigger.java b/main/src/main/java/net/citizensnpcs/trait/waypoint/triggers/DelayTrigger.java index 5c0ac995d..cebb4b5ce 100644 --- a/main/src/main/java/net/citizensnpcs/trait/waypoint/triggers/DelayTrigger.java +++ b/main/src/main/java/net/citizensnpcs/trait/waypoint/triggers/DelayTrigger.java @@ -22,7 +22,7 @@ public DelayTrigger(int delay) { private void delay(WaypointProvider provider) { provider.setPaused(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> provider.setPaused(false), delay); + CitizensAPI.getScheduler().runTaskLater(() -> provider.setPaused(false), delay); } @Override From 77ebfdf110b257b2e47c368f767c9c0471cf0999 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:42:37 +0100 Subject: [PATCH 24/50] Replace scheduler dans PlayerAnimation.java --- .../java/net/citizensnpcs/util/PlayerAnimation.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java b/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java index 4cd11fc71..c1cd500bc 100644 --- a/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java +++ b/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java @@ -80,7 +80,7 @@ public void play(Player player, Supplier> to) { final NPC holder = CitizensAPI.getTemporaryNPCRegistry().createNPC(EntityType.ARMOR_STAND, ""); holder.getOrAddTrait(ArmorStandTrait.class).setAsPointEntity(); holder.spawn(player.getLocation()); - new BukkitRunnable() { + new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { @Override public void cancel() { super.cancel(); @@ -98,7 +98,7 @@ public void run() { NMS.mount(holder.getEntity(), player); } } - }.runTaskTimer(CitizensAPI.getPlugin(), 0, 1); + }.runEntityTaskTimer(CitizensAPI.getPlugin(), holder.getEntity(), null, 0, 1); return; } else if (this == STOP_SITTING) { if (player instanceof NPCHolder) { @@ -125,7 +125,8 @@ public void run() { } else if (this == STOP_USE_ITEM || this == START_USE_MAINHAND_ITEM || this == START_USE_OFFHAND_ITEM) { NMS.playAnimation(this, player, to.get()); if (player.hasMetadata("citizens-using-item-id")) { - Bukkit.getScheduler().cancelTask(player.getMetadata("citizens-using-item-id").get(0).asInt()); + net.citizensnpcs.api.util.schedulers.SchedulerTask task = (net.citizensnpcs.api.util.schedulers.SchedulerTask) player.getMetadata("citizens-using-item-id").get(0).value(); + if (task != null) task.cancel(); player.removeMetadata("citizens-using-item-id", CitizensAPI.getPlugin()); } if (this == STOP_USE_ITEM) @@ -136,7 +137,7 @@ public void run() { if (using != null && BAD_ITEMS_TO_USE.contains(using.getType()) && player.hasMetadata("citizens-using-item-remaining-ticks")) { int remainingTicks = player.getMetadata("citizens-using-item-remaining-ticks").get(0).asInt(); - new BukkitRunnable() { + new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { @Override public void run() { if (!NMS.isValid(player)) { @@ -150,7 +151,7 @@ public void run() { new FixedMetadataValue(CitizensAPI.getPlugin(), getTaskId())); } } - }.runTaskTimer(CitizensAPI.getPlugin(), Math.max(0, remainingTicks + 1), + }.runEntityTaskTimer(CitizensAPI.getPlugin(), player, null, Math.max(0, remainingTicks + 1), Math.max(1, remainingTicks + 1)); } return; From 800fbdc8b2c30f4c2cc769f71b6c9b38ba5badab Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:43:39 +0100 Subject: [PATCH 25/50] Replace scheduler dans PlayerUpdateTask.java --- .../main/java/net/citizensnpcs/util/PlayerUpdateTask.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java b/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java index a258e040a..eb2c7f098 100644 --- a/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java +++ b/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java @@ -19,9 +19,9 @@ import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.trait.PacketNPC; -public class PlayerUpdateTask extends BukkitRunnable { - private final List players = Lists.newArrayList(); - private final Set uuids = Sets.newHashSet(); +public class PlayerUpdateTask extends net.citizensnpcs.api.util.schedulers.SchedulerRunnable { + private final List players = new java.util.concurrent.CopyOnWriteArrayList<>(); + private final Set uuids = java.util.concurrent.ConcurrentHashMap.newKeySet(); @Override public void cancel() { @@ -86,7 +86,7 @@ public PlayerTick(Entity entity, Runnable tick) { @Override public void run() { - tick.run(); + net.citizensnpcs.api.CitizensAPI.getScheduler().runEntityTask(entity, tick); } } From ded99e63b8f784e1981265e9aa3c06dc223f4a73 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:44:31 +0100 Subject: [PATCH 26/50] =?UTF-8?q?callPossiblySync=20peut=20pas=20=C3=AAtre?= =?UTF-8?q?=20appeler=20dans=20Folia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/src/main/java/net/citizensnpcs/util/Util.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main/src/main/java/net/citizensnpcs/util/Util.java b/main/src/main/java/net/citizensnpcs/util/Util.java index f4cc9d1c1..9f33c6e5a 100644 --- a/main/src/main/java/net/citizensnpcs/util/Util.java +++ b/main/src/main/java/net/citizensnpcs/util/Util.java @@ -82,6 +82,9 @@ public static boolean callPistonPushEvent(NPC npc) { } public static T callPossiblySync(Callable callable, boolean sync) { + if (SpigotUtil.isFoliaServer()) { + throw new UnsupportedOperationException("Folia does not have a main thread, so it is not supported."); + } if (!sync) { try { return callable.call(); From f0aba2f3c63528735e6893a8f988a7e3740f52ad Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 19:48:01 +0100 Subject: [PATCH 27/50] =?UTF-8?q?Ajout=20d'une=20v=C3=A9rification=20sur?= =?UTF-8?q?=20certaines=20fonctions=20pour=20savoir=20si=20nous=20sommes?= =?UTF-8?q?=20sur=20le=20bon=20thread?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/net/citizensnpcs/util/NMS.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/util/NMS.java b/main/src/main/java/net/citizensnpcs/util/NMS.java index a7da80826..71204d86f 100644 --- a/main/src/main/java/net/citizensnpcs/util/NMS.java +++ b/main/src/main/java/net/citizensnpcs/util/NMS.java @@ -832,19 +832,35 @@ public static void registerEntityClass(Class clazz, Object type) { } public static void remove(Entity entity) { - BRIDGE.remove(entity); + if (CitizensAPI.getScheduler().isOnOwnerThread(entity)) { + BRIDGE.remove(entity); + } else { + CitizensAPI.getScheduler().runEntityTask(entity, () -> BRIDGE.remove(entity)); + } } public static void removeFromServerPlayerList(Player player) { - BRIDGE.removeFromServerPlayerList(player); + if (CitizensAPI.getScheduler().isOnOwnerThread(player)) { + BRIDGE.removeFromServerPlayerList(player); + } else { + CitizensAPI.getScheduler().runEntityTask(player, () -> BRIDGE.removeFromServerPlayerList(player)); + } } public static void removeFromWorld(org.bukkit.entity.Entity entity) { - BRIDGE.removeFromWorld(entity); + if (CitizensAPI.getScheduler().isOnOwnerThread(entity)) { + BRIDGE.removeFromWorld(entity); + } else { + CitizensAPI.getScheduler().runEntityTask(entity, () -> BRIDGE.removeFromWorld(entity)); + } } public static void removeHookIfNecessary(FishHook entity) { - BRIDGE.removeHookIfNecessary(entity); + if (CitizensAPI.getScheduler().isOnOwnerThread(entity)) { + BRIDGE.removeHookIfNecessary(entity); + } else { + CitizensAPI.getScheduler().runEntityTask(entity, () -> BRIDGE.replaceTrackerEntry(entity)); + } } public static void replaceTracker(Entity entity) { From 1a5992e17383125a9efa8d5deca51835cf483b2c Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 20:16:58 +0100 Subject: [PATCH 28/50] Replace callPossiblySync dans le NMS --- .../v1_20_R4/util/CitizensEntityTracker.java | 60 +++++++++++-------- .../nms/v1_21_R5/entity/HumanController.java | 5 +- .../v1_21_R5/util/CitizensEntityTracker.java | 60 +++++++++++-------- .../v1_21_R6/util/CitizensEntityTracker.java | 60 +++++++++++-------- 4 files changed, 108 insertions(+), 77 deletions(-) diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java index 5417315d1..beceb9dc4 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java @@ -77,36 +77,46 @@ public void updatePlayer(final ServerPlayer entityplayer) { if (REQUIRES_SYNC == null) { REQUIRES_SYNC = !Bukkit.isPrimaryThread(); } - boolean cancelled = Util.callPossiblySync(() -> { - NPCSeenByPlayerEvent event = new NPCSeenByPlayerEvent(npc, entityplayer.getBukkitEntity()); - try { - Bukkit.getPluginManager().callEvent(event); - } catch (IllegalStateException e) { - REQUIRES_SYNC = true; - throw e; - } - if (event.isCancelled()) - return true; - - Integer trackingRange = npc.data(). get(NPC.Metadata.TRACKING_RANGE); - if (TRACKING_RANGE_SETTER != null && trackingRange != null - && npc.data().get("last-tracking-range", -1) != trackingRange.intValue()) { - try { - TRACKING_RANGE_SETTER.invoke(CitizensEntityTracker.this, trackingRange); - npc.data().set("last-tracking-range", trackingRange); - } catch (Throwable e) { - e.printStackTrace(); - } + cancelledUpdatePlayer(npc, entityplayer, cancelled -> { + if (cancelled) { + return; } - return false; - }, REQUIRES_SYNC); - - if (cancelled) - return; + super.updatePlayer(entityplayer); + }); } super.updatePlayer(entityplayer); } + private void cancelledUpdatePlayer(final NPC npc, + final ServerPlayer entityplayer, + final java.util.function.Consumer callback) { + net.citizensnpcs.api.CitizensAPI.getScheduler().runEntityTask(entityplayer.getBukkitEntity(), () -> { + NPCSeenByPlayerEvent event = new NPCSeenByPlayerEvent(npc, entityplayer.getBukkitEntity()); + try { + Bukkit.getPluginManager().callEvent(event); + } catch (IllegalStateException e) { + REQUIRES_SYNC = true; + throw e; + } + if (event.isCancelled()) { + callback.accept(true); + return; + } + + Integer trackingRange = npc.data().get(NPC.Metadata.TRACKING_RANGE); + if (TRACKING_RANGE_SETTER != null && trackingRange != null + && npc.data().get("last-tracking-range", -1) != trackingRange.intValue()) { + try { + TRACKING_RANGE_SETTER.invoke(CitizensEntityTracker.this, trackingRange); + npc.data().set("last-tracking-range", trackingRange); + } catch (Throwable e) { + e.printStackTrace(); + } + } + callback.accept(false); + }); + } + public static Collection getSeenBy(TrackedEntity tracker) { return tracker.seenBy.stream().map(c -> c.getPlayer().getBukkitEntity()).collect(Collectors.toSet()); } diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/entity/HumanController.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/entity/HumanController.java index b70d491b8..5e0dab722 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/entity/HumanController.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/entity/HumanController.java @@ -54,11 +54,12 @@ protected Entity createEntity(final Location at, final NPC npc) { e.printStackTrace(); } } - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runTaskLater(() -> { + if (getBukkitEntity() == null) return; if (getBukkitEntity() == null || !getBukkitEntity().isValid() || getBukkitEntity() != handle.getBukkitEntity()) return; - NMS.addOrRemoveFromPlayerList(getBukkitEntity(), npc.shouldRemoveFromPlayerList()); + CitizensAPI.getScheduler().runEntityTask(getBukkitEntity(), () -> NMS.addOrRemoveFromPlayerList(getBukkitEntity(), npc.shouldRemoveFromPlayerList())); }, 20); handle.getBukkitEntity().setSleepingIgnored(true); return handle.getBukkitEntity(); diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java index 3d16b0b17..d5018e0ae 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java @@ -77,36 +77,46 @@ public void updatePlayer(final ServerPlayer entityplayer) { if (REQUIRES_SYNC == null) { REQUIRES_SYNC = !Bukkit.isPrimaryThread(); } - boolean cancelled = Util.callPossiblySync(() -> { - NPCSeenByPlayerEvent event = new NPCSeenByPlayerEvent(npc, entityplayer.getBukkitEntity()); - try { - Bukkit.getPluginManager().callEvent(event); - } catch (IllegalStateException e) { - REQUIRES_SYNC = true; - throw e; - } - if (event.isCancelled()) - return true; - - Integer trackingRange = npc.data(). get(NPC.Metadata.TRACKING_RANGE); - if (TRACKING_RANGE_SETTER != null && trackingRange != null - && npc.data().get("last-tracking-range", -1) != trackingRange.intValue()) { - try { - TRACKING_RANGE_SETTER.invoke(CitizensEntityTracker.this, trackingRange); - npc.data().set("last-tracking-range", trackingRange); - } catch (Throwable e) { - e.printStackTrace(); - } + cancelledUpdatePlayer(npc, entityplayer, cancelled -> { + if (cancelled) { + return; } - return false; - }, REQUIRES_SYNC); - - if (cancelled) - return; + super.updatePlayer(entityplayer); + }); } super.updatePlayer(entityplayer); } + private void cancelledUpdatePlayer(final NPC npc, + final ServerPlayer entityplayer, + final java.util.function.Consumer callback) { + net.citizensnpcs.api.CitizensAPI.getScheduler().runEntityTask(entityplayer.getBukkitEntity(), () -> { + NPCSeenByPlayerEvent event = new NPCSeenByPlayerEvent(npc, entityplayer.getBukkitEntity()); + try { + Bukkit.getPluginManager().callEvent(event); + } catch (IllegalStateException e) { + REQUIRES_SYNC = true; + throw e; + } + if (event.isCancelled()) { + callback.accept(true); + return; + } + + Integer trackingRange = npc.data().get(NPC.Metadata.TRACKING_RANGE); + if (TRACKING_RANGE_SETTER != null && trackingRange != null + && npc.data().get("last-tracking-range", -1) != trackingRange.intValue()) { + try { + TRACKING_RANGE_SETTER.invoke(CitizensEntityTracker.this, trackingRange); + npc.data().set("last-tracking-range", trackingRange); + } catch (Throwable e) { + e.printStackTrace(); + } + } + callback.accept(false); + }); + } + public static Collection getSeenBy(TrackedEntity tracker) { return tracker.seenBy.stream().map(c -> c.getPlayer().getBukkitEntity()).collect(Collectors.toSet()); } diff --git a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java index 91256418f..9cadd97d9 100644 --- a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java +++ b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java @@ -77,36 +77,46 @@ public void updatePlayer(final ServerPlayer entityplayer) { if (REQUIRES_SYNC == null) { REQUIRES_SYNC = !Bukkit.isPrimaryThread(); } - boolean cancelled = Util.callPossiblySync(() -> { - NPCSeenByPlayerEvent event = new NPCSeenByPlayerEvent(npc, entityplayer.getBukkitEntity()); - try { - Bukkit.getPluginManager().callEvent(event); - } catch (IllegalStateException e) { - REQUIRES_SYNC = true; - throw e; - } - if (event.isCancelled()) - return true; - - Integer trackingRange = npc.data(). get(NPC.Metadata.TRACKING_RANGE); - if (TRACKING_RANGE_SETTER != null && trackingRange != null - && npc.data().get("last-tracking-range", -1) != trackingRange.intValue()) { - try { - TRACKING_RANGE_SETTER.invoke(CitizensEntityTracker.this, trackingRange); - npc.data().set("last-tracking-range", trackingRange); - } catch (Throwable e) { - e.printStackTrace(); - } + cancelledUpdatePlayer(npc, entityplayer, cancelled -> { + if (cancelled) { + return; } - return false; - }, REQUIRES_SYNC); - - if (cancelled) - return; + super.updatePlayer(entityplayer); + }); } super.updatePlayer(entityplayer); } + private void cancelledUpdatePlayer(final NPC npc, + final ServerPlayer entityplayer, + final java.util.function.Consumer callback) { + net.citizensnpcs.api.CitizensAPI.getScheduler().runEntityTask(entityplayer.getBukkitEntity(), () -> { + NPCSeenByPlayerEvent event = new NPCSeenByPlayerEvent(npc, entityplayer.getBukkitEntity()); + try { + Bukkit.getPluginManager().callEvent(event); + } catch (IllegalStateException e) { + REQUIRES_SYNC = true; + throw e; + } + if (event.isCancelled()) { + callback.accept(true); + return; + } + + Integer trackingRange = npc.data().get(NPC.Metadata.TRACKING_RANGE); + if (TRACKING_RANGE_SETTER != null && trackingRange != null + && npc.data().get("last-tracking-range", -1) != trackingRange.intValue()) { + try { + TRACKING_RANGE_SETTER.invoke(CitizensEntityTracker.this, trackingRange); + npc.data().set("last-tracking-range", trackingRange); + } catch (Throwable e) { + e.printStackTrace(); + } + } + callback.accept(false); + }); + } + public static Collection getSeenBy(TrackedEntity tracker) { return tracker.seenBy.stream().map(c -> c.getPlayer().getBukkitEntity()).collect(Collectors.toSet()); } From 71c926b114d3e9d2b84d3a0fb196ad697642ce89 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 20:58:09 +0100 Subject: [PATCH 29/50] Support Tracker entity folia --- .../nms/v1_20_R4/util/NMSImpl.java | 51 +++++++++++++++++-- .../nms/v1_21_R5/util/NMSImpl.java | 51 +++++++++++++++++-- .../nms/v1_21_R6/util/NMSImpl.java | 51 +++++++++++++++++-- 3 files changed, 144 insertions(+), 9 deletions(-) diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java index 87b6065d6..88eef2c5f 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java @@ -1379,7 +1379,21 @@ public void removeHookIfNecessary(FishHook entity) { @Override public void replaceTrackerEntry(org.bukkit.entity.Entity entity) { ServerLevel server = (ServerLevel) getHandle(entity).level(); - TrackedEntity entry = server.getChunkSource().chunkMap.entityMap.get(entity.getEntityId()); + TrackedEntity entry; + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + try { + Entity handle = getHandle(entity); + entry = getTrackedEntityFolia(handle); + if (entry == null) return; + entry.broadcastRemoved(); + CitizensEntityTracker newTracker = new CitizensEntityTracker(server.getChunkSource().chunkMap, entry); + setTrackedEntityFolia(handle, newTracker); + } catch (Exception exception) { + exception.printStackTrace(System.err); + } + return; + } + entry = server.getChunkSource().chunkMap.entityMap.get(entity.getEntityId()); if (entry == null) return; entry.broadcastRemoved(); @@ -1577,6 +1591,11 @@ public void setKnockbackResistance(org.bukkit.entity.LivingEntity entity, double @Override public void setLocationDirectly(org.bukkit.entity.Entity entity, Location location) { + // Todo temp teleport + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); + return; + } getHandle(entity).moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); } @@ -2361,8 +2380,13 @@ public static List> getPositionUpdate(org.bukkit.entity.Entity from, b } List> toSend = Lists.newArrayList(); if (position) { - TrackedEntity entry = ((ServerLevel) handle.level()).getChunkSource().chunkMap.entityMap - .get(handle.getId()); + TrackedEntity entry = null; + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + entry = getTrackedEntityFolia(handle); + } else { + entry = ((ServerLevel) handle.level()).getChunkSource().chunkMap.entityMap + .get(handle.getId()); + } if (entry == null) { Messaging.debug("Null tracker entity for ", from); return Collections.emptyList(); @@ -2759,4 +2783,25 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { Messaging.logTr(Messages.ERROR_GETTING_ID_MAPPING, e.getMessage()); } } + + private static TrackedEntity getTrackedEntityFolia(Entity entity) { + try { + java.lang.reflect.Field field = Entity.class.getDeclaredField("tracker"); + field.setAccessible(true); + return (TrackedEntity) field.get(entity); + } catch (Throwable e) { + e.printStackTrace(System.err); + } + return null; + } + + private void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { + try { + java.lang.reflect.Field field = Entity.class.getDeclaredField("tracker"); + field.setAccessible(true); + field.set(entity, trackedEntity); + } catch (Throwable e) { + e.printStackTrace(System.err); + } + } } diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java index 2fef11855..d9fb4fcab 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java @@ -1369,7 +1369,21 @@ public void removeHookIfNecessary(FishHook entity) { public void replaceTrackerEntry(org.bukkit.entity.Entity entity) { Entity handle = getHandle(entity); ChunkMap cm = ((ServerLevel) handle.level()).getChunkSource().chunkMap; - TrackedEntity entry = cm.entityMap.get(entity.getEntityId()); + TrackedEntity entry; + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + try { + ServerLevel server = (ServerLevel) getHandle(entity).level(); + entry = getTrackedEntityFolia(handle); + if (entry == null) return; + entry.broadcastRemoved(); + CitizensEntityTracker newTracker = new CitizensEntityTracker(server.getChunkSource().chunkMap, entry); + setTrackedEntityFolia(handle, newTracker); + } catch (Exception exception) { + exception.printStackTrace(System.err); + } + return; + } + entry = cm.entityMap.get(entity.getEntityId()); if (entry == null) return; entry.broadcastRemoved(); @@ -1575,6 +1589,11 @@ public void setKnockbackResistance(org.bukkit.entity.LivingEntity entity, double @Override public void setLocationDirectly(org.bukkit.entity.Entity entity, Location location) { + // Todo - temp teleport + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); + return; + } getHandle(entity).snapTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); } @@ -2260,8 +2279,13 @@ public static List> getPositionUpdate(org.bukkit.entity.Entity from, b } List> toSend = Lists.newArrayList(); if (position) { - TrackedEntity entry = ((ServerLevel) handle.level()).getChunkSource().chunkMap.entityMap - .get(handle.getId()); + TrackedEntity entry = null; + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + entry = getTrackedEntityFolia(handle); + } else { + entry = ((ServerLevel) handle.level()).getChunkSource().chunkMap.entityMap + .get(handle.getId()); + } if (entry == null) { Messaging.debug("Null tracker entity for ", from); return Collections.emptyList(); @@ -2789,4 +2813,25 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { Messaging.logTr(Messages.ERROR_GETTING_ID_MAPPING, e.getMessage()); } } + + private static TrackedEntity getTrackedEntityFolia(Entity entity) { + try { + java.lang.reflect.Field field = Entity.class.getDeclaredField("trackedEntity"); + field.setAccessible(true); + return (TrackedEntity) field.get(entity); + } catch (Throwable e) { + e.printStackTrace(System.err); + } + return null; + } + + private void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { + try { + java.lang.reflect.Field field = Entity.class.getDeclaredField("trackedEntity"); + field.setAccessible(true); + field.set(entity, trackedEntity); + } catch (Throwable e) { + e.printStackTrace(System.err); + } + } } diff --git a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java index e733d123a..4bacc14d5 100644 --- a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java +++ b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java @@ -1405,7 +1405,21 @@ public void removeHookIfNecessary(FishHook entity) { public void replaceTrackerEntry(org.bukkit.entity.Entity entity) { Entity handle = getHandle(entity); ChunkMap cm = ((ServerLevel) handle.level()).getChunkSource().chunkMap; - TrackedEntity entry = cm.entityMap.get(entity.getEntityId()); + TrackedEntity entry; + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + try { + ServerLevel server = (ServerLevel) getHandle(entity).level(); + entry = getTrackedEntityFolia(handle); + if (entry == null) return; + entry.broadcastRemoved(); + CitizensEntityTracker newTracker = new CitizensEntityTracker(server.getChunkSource().chunkMap, entry); + setTrackedEntityFolia(handle, newTracker); + } catch (Exception exception) { + exception.printStackTrace(System.err); + } + return; + } + entry = cm.entityMap.get(entity.getEntityId()); if (entry == null) return; entry.broadcastRemoved(); @@ -1611,6 +1625,11 @@ public void setKnockbackResistance(org.bukkit.entity.LivingEntity entity, double @Override public void setLocationDirectly(org.bukkit.entity.Entity entity, Location location) { + // Todo - temp teleport + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); + return; + } getHandle(entity).snapTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); } @@ -2300,8 +2319,13 @@ public static List> getPositionUpdate(org.bukkit.entity.Entity from, b } List> toSend = Lists.newArrayList(); if (position) { - TrackedEntity entry = ((ServerLevel) handle.level()).getChunkSource().chunkMap.entityMap - .get(handle.getId()); + TrackedEntity entry = null; + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + entry = getTrackedEntityFolia(handle); + } else { + entry = ((ServerLevel) handle.level()).getChunkSource().chunkMap.entityMap + .get(handle.getId()); + } if (entry == null) { Messaging.debug("Null tracker entity for ", from); return Collections.emptyList(); @@ -2840,4 +2864,25 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { Messaging.logTr(Messages.ERROR_GETTING_ID_MAPPING, e.getMessage()); } } + + private static TrackedEntity getTrackedEntityFolia(Entity entity) { + try { + java.lang.reflect.Field field = Entity.class.getDeclaredField("trackedEntity"); + field.setAccessible(true); + return (TrackedEntity) field.get(entity); + } catch (Throwable e) { + e.printStackTrace(System.err); + } + return null; + } + + private void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { + try { + java.lang.reflect.Field field = Entity.class.getDeclaredField("trackedEntity"); + field.setAccessible(true); + field.set(entity, trackedEntity); + } catch (Throwable e) { + e.printStackTrace(System.err); + } + } } From 6ba15fccb2dfe80c199db776c133995cbacd903b Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 21:00:37 +0100 Subject: [PATCH 30/50] Replace scheduler dans NMS EntityHumanNPC --- .../net/citizensnpcs/nms/v1_20_R4/entity/EntityHumanNPC.java | 4 ++-- .../net/citizensnpcs/nms/v1_21_R5/entity/EntityHumanNPC.java | 4 ++-- .../net/citizensnpcs/nms/v1_21_R6/entity/EntityHumanNPC.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/entity/EntityHumanNPC.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/entity/EntityHumanNPC.java index a188be5fb..06627ccd6 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/entity/EntityHumanNPC.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/entity/EntityHumanNPC.java @@ -122,7 +122,7 @@ public void die(DamageSource damagesource) { return; super.die(damagesource); - Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runEntityTaskLater(EntityHumanNPC.this.getBukkitEntity(), () -> { ((ServerLevel) level()).removePlayerImmediately(EntityHumanNPC.this, RemovalReason.KILLED); ((ServerLevel) level()).getChunkSource().removeEntity(EntityHumanNPC.this); }, 15); // give enough time for death and smoke animation @@ -268,7 +268,7 @@ public boolean hurt(DamageSource damagesource, float f) { boolean damaged = super.hurt(damagesource, f); if (damaged && hurtMarked) { hurtMarked = false; - Bukkit.getScheduler().runTask(CitizensAPI.getPlugin(), () -> EntityHumanNPC.this.hurtMarked = true); + CitizensAPI.getScheduler().runEntityTask(EntityHumanNPC.this.getBukkitEntity(), () -> EntityHumanNPC.this.hurtMarked = true); } return damaged; } diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/entity/EntityHumanNPC.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/entity/EntityHumanNPC.java index 862319d3d..2042dcc83 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/entity/EntityHumanNPC.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/entity/EntityHumanNPC.java @@ -123,7 +123,7 @@ public void die(DamageSource damagesource) { return; super.die(damagesource); - Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runEntityTaskLater(EntityHumanNPC.this.getBukkitEntity(), () -> { level().removePlayerImmediately(EntityHumanNPC.this, RemovalReason.KILLED); level().getChunkSource().removeEntity(EntityHumanNPC.this); }, 15); // give enough time for death and smoke animation @@ -270,7 +270,7 @@ public boolean hurtServer(ServerLevel level, DamageSource damagesource, float f) boolean damaged = super.hurtServer(level, damagesource, f); if (damaged && hurtMarked) { hurtMarked = false; - Bukkit.getScheduler().runTask(CitizensAPI.getPlugin(), () -> EntityHumanNPC.this.hurtMarked = true); + CitizensAPI.getScheduler().runEntityTask(EntityHumanNPC.this.getBukkitEntity(), () -> EntityHumanNPC.this.hurtMarked = true); } return damaged; } diff --git a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/entity/EntityHumanNPC.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/entity/EntityHumanNPC.java index 971542f26..30033a3f2 100644 --- a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/entity/EntityHumanNPC.java +++ b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/entity/EntityHumanNPC.java @@ -120,7 +120,7 @@ public void die(DamageSource damagesource) { return; super.die(damagesource); - Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), () -> { + CitizensAPI.getScheduler().runEntityTaskLater(EntityHumanNPC.this.getBukkitEntity(), () -> { level().removePlayerImmediately(EntityHumanNPC.this, RemovalReason.KILLED); level().getChunkSource().removeEntity(EntityHumanNPC.this); }, 15); // give enough time for death and smoke animation @@ -267,7 +267,7 @@ public boolean hurtServer(ServerLevel level, DamageSource damagesource, float f) boolean damaged = super.hurtServer(level, damagesource, f); if (damaged && hurtMarked) { hurtMarked = false; - Bukkit.getScheduler().runTask(CitizensAPI.getPlugin(), () -> EntityHumanNPC.this.hurtMarked = true); + CitizensAPI.getScheduler().runEntityTask(EntityHumanNPC.this.getBukkitEntity(), () -> EntityHumanNPC.this.hurtMarked = true); } return damaged; } From 12f2fdac4f8f80cce649e2632ffaff8f7928d0d2 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 22:11:03 +0100 Subject: [PATCH 31/50] NPCs can spawn --- .../java/net/citizensnpcs/EventListen.java | 1 - .../npc/AbstractEntityController.java | 5 ++++ .../net/citizensnpcs/npc/CitizensNPC.java | 8 +++++- .../citizensnpcs/npc/EntityController.java | 2 ++ .../net/citizensnpcs/trait/PacketNPC.java | 5 ++++ .../main/java/net/citizensnpcs/util/NMS.java | 10 ++++++- .../java/net/citizensnpcs/util/NMSBridge.java | 2 ++ .../nms/v1_20_R4/util/NMSImpl.java | 27 +++++++++++++++++++ .../nms/v1_21_R5/util/NMSImpl.java | 27 +++++++++++++++++++ .../nms/v1_21_R6/util/NMSImpl.java | 27 +++++++++++++++++++ 10 files changed, 111 insertions(+), 3 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/EventListen.java b/main/src/main/java/net/citizensnpcs/EventListen.java index 269dd603a..8af1ab830 100644 --- a/main/src/main/java/net/citizensnpcs/EventListen.java +++ b/main/src/main/java/net/citizensnpcs/EventListen.java @@ -64,7 +64,6 @@ import org.bukkit.event.world.WorldUnloadEvent; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.RegisteredListener; -import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; import com.google.common.base.Joiner; diff --git a/main/src/main/java/net/citizensnpcs/npc/AbstractEntityController.java b/main/src/main/java/net/citizensnpcs/npc/AbstractEntityController.java index c86dbcead..075bd1009 100644 --- a/main/src/main/java/net/citizensnpcs/npc/AbstractEntityController.java +++ b/main/src/main/java/net/citizensnpcs/npc/AbstractEntityController.java @@ -61,4 +61,9 @@ public void remove() { public boolean spawn(Location at) { return !Util.isLoaded(at) ? false : NMS.addEntityToWorld(bukkitEntity, CreatureSpawnEvent.SpawnReason.CUSTOM); } + + @Override + public void spawn(Location at, java.util.function.Consumer isSpawned) { + NMS.addEntityToWorld(bukkitEntity, CreatureSpawnEvent.SpawnReason.CUSTOM, isSpawned); + } } \ No newline at end of file diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index f34964441..ea575a155 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -328,8 +328,14 @@ public boolean spawn(Location at, SpawnReason reason, Consumer callback) } data().set(NPC.Metadata.NPC_SPAWNING_IN_PROGRESS, true); boolean wasLoaded = Messaging.isDebugging() ? Util.isLoaded(at) : false; - boolean couldSpawn = entityController.spawn(at); + final Location location = at; + entityController.spawn(location, couldSpawn -> { + this.spawn(couldSpawn, reason, wasLoaded, location, callback); + }); + return true; // Todo : Can not determine success yet + } + private boolean spawn(boolean couldSpawn, SpawnReason reason, boolean wasLoaded, Location at, Consumer callback) { if (!couldSpawn) { if (Messaging.isDebugging()) { Messaging.debug("Retrying spawn of", this, "later, SpawnReason." + reason + ". Was loaded", wasLoaded, diff --git a/main/src/main/java/net/citizensnpcs/npc/EntityController.java b/main/src/main/java/net/citizensnpcs/npc/EntityController.java index 23a373fab..fae9f03ab 100644 --- a/main/src/main/java/net/citizensnpcs/npc/EntityController.java +++ b/main/src/main/java/net/citizensnpcs/npc/EntityController.java @@ -15,4 +15,6 @@ public interface EntityController { void remove(); boolean spawn(Location at); + + void spawn(Location at, java.util.function.Consumer callback); } diff --git a/main/src/main/java/net/citizensnpcs/trait/PacketNPC.java b/main/src/main/java/net/citizensnpcs/trait/PacketNPC.java index e61b42ada..6a7cb9ca2 100644 --- a/main/src/main/java/net/citizensnpcs/trait/PacketNPC.java +++ b/main/src/main/java/net/citizensnpcs/trait/PacketNPC.java @@ -114,5 +114,10 @@ public boolean spawn(Location at) { PlayerUpdateTask.register(getBukkitEntity()); return true; } + + @Override + public void spawn(Location at, java.util.function.Consumer callback) { + callback.accept(spawn(at)); + } } } \ No newline at end of file diff --git a/main/src/main/java/net/citizensnpcs/util/NMS.java b/main/src/main/java/net/citizensnpcs/util/NMS.java index 71204d86f..cdb06f5d9 100644 --- a/main/src/main/java/net/citizensnpcs/util/NMS.java +++ b/main/src/main/java/net/citizensnpcs/util/NMS.java @@ -92,6 +92,10 @@ public static boolean addEntityToWorld(org.bukkit.entity.Entity entity, SpawnRea return BRIDGE.addEntityToWorld(entity, custom); } + public static void addEntityToWorld(org.bukkit.entity.Entity entity, SpawnReason custom, Consumer isAdded) { + BRIDGE.addEntityToWorld(entity, custom, isAdded); + } + public static void addOrRemoveFromPlayerList(org.bukkit.entity.Entity entity, boolean remove) { BRIDGE.addOrRemoveFromPlayerList(entity, remove); } @@ -1044,7 +1048,11 @@ public static void setSnifferState(Entity entity, SnifferState state) { } public static void setStepHeight(org.bukkit.entity.Entity entity, float height) { - BRIDGE.setStepHeight(entity, height); + if (CitizensAPI.getScheduler().isOnOwnerThread(entity)) { + BRIDGE.setStepHeight(entity, height); + } else { + CitizensAPI.getScheduler().runEntityTask(entity, () -> BRIDGE.setStepHeight(entity, height)); + } } public static void setTeamNameTagVisible(Team team, boolean visible) { diff --git a/main/src/main/java/net/citizensnpcs/util/NMSBridge.java b/main/src/main/java/net/citizensnpcs/util/NMSBridge.java index 004ab3c13..3b17f7019 100644 --- a/main/src/main/java/net/citizensnpcs/util/NMSBridge.java +++ b/main/src/main/java/net/citizensnpcs/util/NMSBridge.java @@ -54,6 +54,8 @@ default void activate(Entity entity) { public boolean addEntityToWorld(Entity entity, SpawnReason custom); + public void addEntityToWorld(Entity entity, SpawnReason custom, java.util.function.Consumer isAdded); + public void addOrRemoveFromPlayerList(Entity entity, boolean remove); public void attack(LivingEntity attacker, LivingEntity target); diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java index 88eef2c5f..2d9fd52a0 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java @@ -409,6 +409,33 @@ public boolean addEntityToWorld(org.bukkit.entity.Entity entity, SpawnReason cus return success; } + @Override + public void addEntityToWorld(org.bukkit.entity.Entity entity, SpawnReason custom, java.util.function.Consumer isAdded) { + if (!net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + isAdded.accept(addEntityToWorld(entity, custom)); + return; + } + final Location location = entity.getLocation(); + CitizensAPI.getScheduler().runRegionTask(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, () -> { + final Entity entityNMS = getHandle(entity); + final ServerLevel serverLevel = (ServerLevel) entityNMS.level(); + int viewDistance = -1; + ChunkMap chunkMap = null; + if (entity instanceof Player) { + chunkMap = serverLevel.getChunkSource().chunkMap; + viewDistance = chunkMap.serverViewDistance; + chunkMap.serverViewDistance = -1; + } + final ChunkMap chunkMapFinal = chunkMap; + final int viewDistanceFinal = viewDistance; + boolean success = serverLevel.addFreshEntity(getHandle(entity), custom); + if (chunkMapFinal != null) { + chunkMapFinal.serverViewDistance = viewDistanceFinal; + } + isAdded.accept(success); + }); + } + @Override public void addOrRemoveFromPlayerList(org.bukkit.entity.Entity entity, boolean remove) { if (entity == null) diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java index 4e79738a7..ff9b2459c 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java @@ -422,6 +422,33 @@ public boolean addEntityToWorld(org.bukkit.entity.Entity entity, SpawnReason cus return success; } + @Override + public void addEntityToWorld(org.bukkit.entity.Entity entity, SpawnReason custom, java.util.function.Consumer isAdded) { + if (!net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + isAdded.accept(addEntityToWorld(entity, custom)); + return; + } + final Location location = entity.getLocation(); + CitizensAPI.getScheduler().runRegionTask(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, () -> { + final Entity entityNMS = getHandle(entity); + final ServerLevel serverLevel = (ServerLevel) entityNMS.level(); + int viewDistance = -1; + ChunkMap chunkMap = null; + if (entity instanceof Player) { + chunkMap = serverLevel.getChunkSource().chunkMap; + viewDistance = chunkMap.serverViewDistance; + chunkMap.serverViewDistance = -1; + } + final ChunkMap chunkMapFinal = chunkMap; + final int viewDistanceFinal = viewDistance; + boolean success = serverLevel.addFreshEntity(getHandle(entity), custom); + if (chunkMapFinal != null) { + chunkMapFinal.serverViewDistance = viewDistanceFinal; + } + isAdded.accept(success); + }); + } + @Override public void addOrRemoveFromPlayerList(org.bukkit.entity.Entity entity, boolean remove) { if (entity == null) diff --git a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java index 76c998ec5..7d8fba694 100644 --- a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java +++ b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java @@ -432,6 +432,33 @@ public boolean addEntityToWorld(org.bukkit.entity.Entity entity, SpawnReason cus return success; } + @Override + public void addEntityToWorld(org.bukkit.entity.Entity entity, SpawnReason custom, java.util.function.Consumer isAdded) { + if (!net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + isAdded.accept(addEntityToWorld(entity, custom)); + return; + } + final Location location = entity.getLocation(); + CitizensAPI.getScheduler().runRegionTask(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, () -> { + final Entity entityNMS = getHandle(entity); + final ServerLevel serverLevel = (ServerLevel) entityNMS.level(); + int viewDistance = -1; + ChunkMap chunkMap = null; + if (entity instanceof Player) { + chunkMap = serverLevel.getChunkSource().chunkMap; + viewDistance = chunkMap.serverViewDistance; + chunkMap.serverViewDistance = -1; + } + final ChunkMap chunkMapFinal = chunkMap; + final int viewDistanceFinal = viewDistance; + boolean success = serverLevel.addFreshEntity(getHandle(entity), custom); + if (chunkMapFinal != null) { + chunkMapFinal.serverViewDistance = viewDistanceFinal; + } + isAdded.accept(success); + }); + } + @Override public void addOrRemoveFromPlayerList(org.bukkit.entity.Entity entity, boolean remove) { if (entity == null) From daf51cabf27ae4c7276177d0dbf55d030d130e5d Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 28 Oct 2025 22:12:42 +0100 Subject: [PATCH 32/50] Fix teleport not async on folia --- .../main/java/net/citizensnpcs/trait/HologramTrait.java | 8 ++++---- main/src/main/java/net/citizensnpcs/trait/SitTrait.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java b/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java index 9c1cadfd8..682208b3c 100644 --- a/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java @@ -465,7 +465,7 @@ protected void render0(NPC npc, Vector3d offset) { cloud.setRadius(0); cloud.setParticle(Particle.BLOCK_MARKER, Bukkit.createBlockData(Material.AIR)); } - hologram.getEntity().teleport( + SpigotUtil.teleportAsync(hologram.getEntity(), npc.getEntity().getLocation().clone().add(offset.x, offset.y + NMS.getBoundingBoxHeight(npc.getEntity()) - 0.5, offset.z), TeleportCause.PLUGIN); @@ -482,7 +482,7 @@ protected NPC createNPC(NPC base, String name, Vector3d offset) { @Override protected void render0(NPC npc, Vector3d offset) { - hologram.getEntity().teleport(npc.getEntity().getLocation().clone().add(offset.x, + SpigotUtil.teleportAsync(hologram.getEntity(), npc.getEntity().getLocation().clone().add(offset.x, offset.y + NMS.getBoundingBoxHeight(npc.getEntity()), offset.z), TeleportCause.PLUGIN); } } @@ -821,7 +821,7 @@ public Collection getEntities() { @Override protected void render0(NPC npc, Vector3d offset) { - hologram.getEntity().teleport(npc.getEntity().getLocation().clone().add(offset.x, + SpigotUtil.teleportAsync(hologram.getEntity(), npc.getEntity().getLocation().clone().add(offset.x, offset.y + NMS.getBoundingBoxHeight(npc.getEntity()), offset.z), TeleportCause.PLUGIN); } @@ -974,7 +974,7 @@ public void render0(NPC base, Vector3d offset) { disp.setTransformation(tf); } } - hologram.getEntity().teleport( + SpigotUtil.teleportAsync(hologram.getEntity(), base.getEntity().getLocation().clone().add(offset.x, offset.y + NMS.getBoundingBoxHeight(base.getEntity()) + 0.2f, offset.z), TeleportCause.PLUGIN); diff --git a/main/src/main/java/net/citizensnpcs/trait/SitTrait.java b/main/src/main/java/net/citizensnpcs/trait/SitTrait.java index 389b4de04..47c8712fc 100644 --- a/main/src/main/java/net/citizensnpcs/trait/SitTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/SitTrait.java @@ -42,7 +42,7 @@ public void onDespawn() { if (requiresPassengerOffsetCorrection()) { npcLoc = npcLoc.add(0, 0.3, 0); } - npc.getEntity().teleport(npcLoc); + SpigotUtil.teleportAsync(npc.getEntity(), npcLoc); } chair.destroy(); chair = null; From 87759cb5c406937322ff633262daa12a909381fd Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Wed, 29 Oct 2025 05:13:48 +0100 Subject: [PATCH 33/50] Executer une commande sur le bon thread --- main/src/main/java/net/citizensnpcs/util/Util.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/util/Util.java b/main/src/main/java/net/citizensnpcs/util/Util.java index 9f33c6e5a..c48a8ca44 100644 --- a/main/src/main/java/net/citizensnpcs/util/Util.java +++ b/main/src/main/java/net/citizensnpcs/util/Util.java @@ -477,7 +477,9 @@ public static void runCommand(NPC npc, Player clicker, String command, boolean o + " clicker " + clicker); if (!player) { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), interpolatedCommand); + CitizensAPI.getScheduler().runTask(() -> { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), interpolatedCommand); + }); return; } boolean wasOp = clicker.isOp(); From b571f12aa931350ad0cc89b8c152c143d995e983 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Wed, 29 Oct 2025 05:18:39 +0100 Subject: [PATCH 34/50] Fix double appel super.updatePlayer --- .../citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java | 1 + .../citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java | 1 + .../citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java | 1 + 3 files changed, 3 insertions(+) diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java index beceb9dc4..f554a45d5 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java @@ -83,6 +83,7 @@ public void updatePlayer(final ServerPlayer entityplayer) { } super.updatePlayer(entityplayer); }); + return; } super.updatePlayer(entityplayer); } diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java index d5018e0ae..868250848 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java @@ -83,6 +83,7 @@ public void updatePlayer(final ServerPlayer entityplayer) { } super.updatePlayer(entityplayer); }); + return; } super.updatePlayer(entityplayer); } diff --git a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java index 9cadd97d9..027a6a831 100644 --- a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java +++ b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java @@ -83,6 +83,7 @@ public void updatePlayer(final ServerPlayer entityplayer) { } super.updatePlayer(entityplayer); }); + return; } super.updatePlayer(entityplayer); } From da25c585e8aedf939a76922bf7d3d0257ce7ec51 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Wed, 29 Oct 2025 05:35:42 +0100 Subject: [PATCH 35/50] Fix forceload --- main/src/main/java/net/citizensnpcs/util/ChunkCoord.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java b/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java index a3a25356f..c9da5fe13 100644 --- a/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java +++ b/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java @@ -3,6 +3,7 @@ import java.util.Objects; import java.util.UUID; +import net.citizensnpcs.api.CitizensAPI; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; @@ -57,7 +58,7 @@ public void setForceLoaded(boolean b) { return; Chunk chunk = getChunk(); if (chunk != null) { - chunk.setForceLoaded(b); + CitizensAPI.getScheduler().runTask(() -> chunk.setForceLoaded(b)); } } From ae453cb79b0e1665944e458b1a8afe425e743886 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Wed, 29 Oct 2025 05:59:23 +0100 Subject: [PATCH 36/50] =?UTF-8?q?Fix=20spawn=20mauvais=20thread=20pour=20l?= =?UTF-8?q?es=20autres=20entit=C3=A9s=20qui=20ne=20sont=20pas=20des=20play?= =?UTF-8?q?ers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index ea575a155..d095873c8 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -182,7 +182,13 @@ public void load(DataKey root) { if (getOrAddTrait(Spawned.class).shouldSpawn()) { CurrentLocation current = getOrAddTrait(CurrentLocation.class); if (current.getLocation() != null) { - spawn(current.getLocation(), SpawnReason.RESPAWN); + if (CitizensAPI.getScheduler().isOnOwnerThread(current.getLocation())) { + spawn(current.getLocation(), SpawnReason.RESPAWN); + } else { + CitizensAPI.getScheduler().runRegionTask(current.getLocation(), () -> { + spawn(current.getLocation(), SpawnReason.RESPAWN); + }); + } } else if (current.getChunkCoord() != null) { Bukkit.getPluginManager().callEvent(new NPCNeedsRespawnEvent(this, current.getChunkCoord())); } From 3e3b046b73ab6dc91ba9de83a731a266cd193cf1 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Sat, 1 Nov 2025 02:54:53 +0100 Subject: [PATCH 37/50] On ne peux pas Entity#snapTo sur Folia, on fait une teleportation --- .../main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java index ff9b2459c..ff98777d7 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java @@ -1610,7 +1610,6 @@ public void setHeadYaw(org.bukkit.entity.Entity entity, float yaw) { @Override public void setLocationDirectly(org.bukkit.entity.Entity entity, Location location) { - // Todo - temp teleport if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); return; From 3178e348bf11fc02405a43a55ec65f570d3a4172 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Sat, 1 Nov 2025 03:23:39 +0100 Subject: [PATCH 38/50] Resupport spigot /npc create --- .../main/java/net/citizensnpcs/npc/CitizensNPC.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index d095873c8..2205f8111 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -335,10 +335,15 @@ public boolean spawn(Location at, SpawnReason reason, Consumer callback) data().set(NPC.Metadata.NPC_SPAWNING_IN_PROGRESS, true); boolean wasLoaded = Messaging.isDebugging() ? Util.isLoaded(at) : false; final Location location = at; - entityController.spawn(location, couldSpawn -> { - this.spawn(couldSpawn, reason, wasLoaded, location, callback); - }); - return true; // Todo : Can not determine success yet + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + entityController.spawn(location, couldSpawn -> { + this.spawn(couldSpawn, reason, wasLoaded, location, callback); + }); + return true; + } else { + boolean couldSpawn = entityController.spawn(location); + return spawn(couldSpawn, reason, wasLoaded, at, callback); + } } private boolean spawn(boolean couldSpawn, SpawnReason reason, boolean wasLoaded, Location at, Consumer callback) { From 415d8666b75c0dc4ef96f016f4539ffce3c6737b Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Mon, 3 Nov 2025 04:21:07 +0100 Subject: [PATCH 39/50] Fix update ticket --- .../npc/ai/CitizensNavigator.java | 21 +++++++++++++++++-- .../net/citizensnpcs/util/ChunkCoord.java | 6 +++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java b/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java index 673899902..0d38b9206 100644 --- a/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java +++ b/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java @@ -8,6 +8,7 @@ import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; @@ -534,14 +535,30 @@ private void updateTicket(Location target) { // switch ticket to the new chunk if (activeTicket != null) { - activeTicket.getChunk().removePluginChunkTicket(CitizensAPI.getPlugin()); + final ChunkCoord tempTicket = activeTicket; + final World tempWorld = tempTicket.getWorld(); + if (CitizensAPI.getScheduler().isOnOwnerThread(tempWorld, tempTicket.x, tempTicket.z)) { + tempTicket.getChunk().removePluginChunkTicket(CitizensAPI.getPlugin()); + } else { + CitizensAPI.getScheduler().runRegionTask(tempWorld, tempTicket.x, tempTicket.z, () -> { + tempTicket.getChunk().removePluginChunkTicket(CitizensAPI.getPlugin()); + }); + } } if (target == null) { activeTicket = null; return; } activeTicket = coord; - activeTicket.getChunk().addPluginChunkTicket(CitizensAPI.getPlugin()); + final ChunkCoord tempTicket = activeTicket; + final World tempTicketWorld = tempTicket.getWorld(); + if (CitizensAPI.getScheduler().isOnOwnerThread(tempTicketWorld, tempTicket.x, tempTicket.z)) { + tempTicket.getChunk().addPluginChunkTicket(CitizensAPI.getPlugin()); + } else { + CitizensAPI.getScheduler().runRegionTask(tempTicketWorld, tempTicket.x, tempTicket.z, () -> { + tempTicket.getChunk().addPluginChunkTicket(CitizensAPI.getPlugin()); + }); + } } private static boolean SUPPORT_CHUNK_TICKETS = true; diff --git a/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java b/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java index c9da5fe13..1cc2ff02f 100644 --- a/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java +++ b/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java @@ -44,10 +44,14 @@ public boolean equals(Object obj) { } public Chunk getChunk() { - World world = Bukkit.getWorld(worldUUID); + World world = getWorld(); return world != null ? world.getChunkAt(x, z) : null; } + public World getWorld() { + return Bukkit.getWorld(worldUUID); + } + @Override public int hashCode() { return 31 * (31 * (31 + (worldUUID == null ? 0 : worldUUID.hashCode())) + x) + z; From a6249a7c10c7e912dac2045b33c7c38555bee326 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 4 Nov 2025 21:04:10 +0100 Subject: [PATCH 40/50] COW ne permet pas de remove (UnsupportedOperationException) --- .../main/java/net/citizensnpcs/util/PlayerUpdateTask.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java b/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java index eb2c7f098..cb9f886a8 100644 --- a/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java +++ b/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java @@ -20,7 +20,7 @@ import net.citizensnpcs.trait.PacketNPC; public class PlayerUpdateTask extends net.citizensnpcs.api.util.schedulers.SchedulerRunnable { - private final List players = new java.util.concurrent.CopyOnWriteArrayList<>(); + private final java.util.Queue players = new java.util.concurrent.ConcurrentLinkedQueue<>(); private final Set uuids = java.util.concurrent.ConcurrentHashMap.newKeySet(); @Override @@ -100,6 +100,6 @@ public static void register(org.bukkit.entity.Entity entity) { PLAYERS_PENDING_ADD.add(entity); } - private static final List PLAYERS_PENDING_ADD = new ArrayList<>(); - private static final List PLAYERS_PENDING_REMOVE = new ArrayList<>(); + private static final java.util.Queue PLAYERS_PENDING_ADD = new java.util.concurrent.ConcurrentLinkedQueue<>(); + private static final java.util.Queue PLAYERS_PENDING_REMOVE = new java.util.concurrent.ConcurrentLinkedQueue<>(); } From dbfc5a1b62b546aed7465262652f859114e08d25 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Mon, 24 Nov 2025 17:48:28 +0100 Subject: [PATCH 41/50] Enlever la lambda syntax --- main/src/main/java/net/citizensnpcs/Citizens.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/Citizens.java b/main/src/main/java/net/citizensnpcs/Citizens.java index 10d659485..d8bd8c317 100644 --- a/main/src/main/java/net/citizensnpcs/Citizens.java +++ b/main/src/main/java/net/citizensnpcs/Citizens.java @@ -446,7 +446,7 @@ public void onEnable() { // Setup NPCs after all plugins have been enabled (allows for multiworld // support and for NPCs to properly register external settings) - if (CitizensAPI.getScheduler().runTaskLater(() -> new CitizensLoadTask().run(), 1) == null) { + if (CitizensAPI.getScheduler().runTaskLater(new CitizensLoadTask(), 1) == null) { Messaging.severeTr(Messages.LOAD_TASK_NOT_SCHEDULED); Bukkit.getPluginManager().disablePlugin(this); } @@ -514,7 +514,7 @@ public void removeNamedNPCRegistry(String name) { } private void scheduleSaveTask(int delay) { - CitizensAPI.getScheduler().runTaskTimer(() -> new CitizensSaveTask().run(), delay, delay); + CitizensAPI.getScheduler().runTaskTimer(new CitizensSaveTask(), delay, delay); } @Override From 64ad03292160fc802a4b422dee74265e6f2b216d Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Mon, 24 Nov 2025 17:48:50 +0100 Subject: [PATCH 42/50] Fix upstream --- main/src/main/java/net/citizensnpcs/EventListen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/EventListen.java b/main/src/main/java/net/citizensnpcs/EventListen.java index 32cffafde..8aee486f3 100644 --- a/main/src/main/java/net/citizensnpcs/EventListen.java +++ b/main/src/main/java/net/citizensnpcs/EventListen.java @@ -925,7 +925,7 @@ private void registerMoveEvent(Class clazz) { } final Location eventTo = npcMoveEvent.getTo(); if (eventTo.getWorld() != to.getWorld() || eventTo.distance(to) > 0.001) { - CitizensAPI.getScheduler().runEntityTaskLater(plugin, () -> SpigotUtil.teleportAsync(entity, eventTo), 1L); + CitizensAPI.getScheduler().runEntityTaskLater(entity, () -> SpigotUtil.teleportAsync(entity, eventTo), 1L); } } catch (Throwable ex) { ex.printStackTrace(); From 5e5954b19ba16a51eafc064b18531c78418dbd8b Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Mon, 24 Nov 2025 17:50:50 +0100 Subject: [PATCH 43/50] Mettre en import SchedulerRunnable --- main/src/main/java/net/citizensnpcs/EventListen.java | 3 ++- main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java | 3 ++- .../java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java | 7 ++++--- .../main/java/net/citizensnpcs/util/PlayerAnimation.java | 5 +++-- .../main/java/net/citizensnpcs/util/PlayerUpdateTask.java | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/EventListen.java b/main/src/main/java/net/citizensnpcs/EventListen.java index 8aee486f3..8e055cedf 100644 --- a/main/src/main/java/net/citizensnpcs/EventListen.java +++ b/main/src/main/java/net/citizensnpcs/EventListen.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Objects; +import net.citizensnpcs.api.util.schedulers.SchedulerRunnable; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -778,7 +779,7 @@ public void onProjectileHit(ProjectileHitEvent event) { if (!(event.getEntity() instanceof FishHook)) return; NMS.removeHookIfNecessary((FishHook) event.getEntity()); - new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { + new SchedulerRunnable() { int n = 0; @Override diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index 9300e72fc..a9ea2d052 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -7,6 +7,7 @@ import java.util.UUID; import java.util.function.Consumer; +import net.citizensnpcs.api.util.schedulers.SchedulerRunnable; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Registry; @@ -453,7 +454,7 @@ public void accept(Runnable cancel) { postSpawn.accept(() -> { }); } else { - new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { + new SchedulerRunnable() { @Override public void run() { postSpawn.accept(this::cancel); diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java index 3b8c53bc4..8f487d7f9 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java @@ -15,6 +15,7 @@ import javax.annotation.Nullable; +import net.citizensnpcs.api.util.schedulers.SchedulerRunnable; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Entity; @@ -288,7 +289,7 @@ public void updatePlayer(Player player, long delay, boolean reset) { if (player.hasMetadata("NPC")) return; - new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { + new SchedulerRunnable() { @Override public void run() { List visible = getNearbyNPCs(player, reset, false); @@ -304,7 +305,7 @@ public void run() { } // update players when the NPC navigates into their field of view - private class NPCNavigationTracker extends net.citizensnpcs.api.util.schedulers.SchedulerRunnable { + private class NPCNavigationTracker extends SchedulerRunnable { @Override public void run() { if (navigating.isEmpty() || playerTrackers.isEmpty()) @@ -332,7 +333,7 @@ public void run() { // Updates players. Repeating task used to schedule updates without // causing excessive scheduling. - private static class NPCNavigationUpdater extends net.citizensnpcs.api.util.schedulers.SchedulerRunnable { + private static class NPCNavigationUpdater extends SchedulerRunnable { Queue queue = new ArrayDeque<>(20); @Override diff --git a/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java b/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java index c1cd500bc..3fda50f3c 100644 --- a/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java +++ b/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java @@ -4,6 +4,7 @@ import java.util.Set; import java.util.function.Supplier; +import net.citizensnpcs.api.util.schedulers.SchedulerRunnable; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.EntityType; @@ -80,7 +81,7 @@ public void play(Player player, Supplier> to) { final NPC holder = CitizensAPI.getTemporaryNPCRegistry().createNPC(EntityType.ARMOR_STAND, ""); holder.getOrAddTrait(ArmorStandTrait.class).setAsPointEntity(); holder.spawn(player.getLocation()); - new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { + new SchedulerRunnable() { @Override public void cancel() { super.cancel(); @@ -137,7 +138,7 @@ public void run() { if (using != null && BAD_ITEMS_TO_USE.contains(using.getType()) && player.hasMetadata("citizens-using-item-remaining-ticks")) { int remainingTicks = player.getMetadata("citizens-using-item-remaining-ticks").get(0).asInt(); - new net.citizensnpcs.api.util.schedulers.SchedulerRunnable() { + new SchedulerRunnable() { @Override public void run() { if (!NMS.isValid(player)) { diff --git a/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java b/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java index cb9f886a8..a6d8debd3 100644 --- a/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java +++ b/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java @@ -6,6 +6,7 @@ import java.util.Set; import java.util.UUID; +import net.citizensnpcs.api.util.schedulers.SchedulerRunnable; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; @@ -19,7 +20,7 @@ import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.trait.PacketNPC; -public class PlayerUpdateTask extends net.citizensnpcs.api.util.schedulers.SchedulerRunnable { +public class PlayerUpdateTask extends SchedulerRunnable { private final java.util.Queue players = new java.util.concurrent.ConcurrentLinkedQueue<>(); private final Set uuids = java.util.concurrent.ConcurrentHashMap.newKeySet(); From df155cd68bb719c3e5ce505517e7f1f93a17c846 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Mon, 24 Nov 2025 17:56:48 +0100 Subject: [PATCH 44/50] Changer cancelledUpdatePlayer en cancellableUpdatePlayer --- .../citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java | 4 ++-- .../citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java | 4 ++-- .../citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java index f554a45d5..b0d3a7851 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/CitizensEntityTracker.java @@ -77,7 +77,7 @@ public void updatePlayer(final ServerPlayer entityplayer) { if (REQUIRES_SYNC == null) { REQUIRES_SYNC = !Bukkit.isPrimaryThread(); } - cancelledUpdatePlayer(npc, entityplayer, cancelled -> { + cancellableUpdatePlayer(npc, entityplayer, cancelled -> { if (cancelled) { return; } @@ -88,7 +88,7 @@ public void updatePlayer(final ServerPlayer entityplayer) { super.updatePlayer(entityplayer); } - private void cancelledUpdatePlayer(final NPC npc, + private void cancellableUpdatePlayer(final NPC npc, final ServerPlayer entityplayer, final java.util.function.Consumer callback) { net.citizensnpcs.api.CitizensAPI.getScheduler().runEntityTask(entityplayer.getBukkitEntity(), () -> { diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java index 868250848..105530320 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/CitizensEntityTracker.java @@ -77,7 +77,7 @@ public void updatePlayer(final ServerPlayer entityplayer) { if (REQUIRES_SYNC == null) { REQUIRES_SYNC = !Bukkit.isPrimaryThread(); } - cancelledUpdatePlayer(npc, entityplayer, cancelled -> { + cancellableUpdatePlayer(npc, entityplayer, cancelled -> { if (cancelled) { return; } @@ -88,7 +88,7 @@ public void updatePlayer(final ServerPlayer entityplayer) { super.updatePlayer(entityplayer); } - private void cancelledUpdatePlayer(final NPC npc, + private void cancellableUpdatePlayer(final NPC npc, final ServerPlayer entityplayer, final java.util.function.Consumer callback) { net.citizensnpcs.api.CitizensAPI.getScheduler().runEntityTask(entityplayer.getBukkitEntity(), () -> { diff --git a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java index 027a6a831..5adea2c8c 100644 --- a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java +++ b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/CitizensEntityTracker.java @@ -77,7 +77,7 @@ public void updatePlayer(final ServerPlayer entityplayer) { if (REQUIRES_SYNC == null) { REQUIRES_SYNC = !Bukkit.isPrimaryThread(); } - cancelledUpdatePlayer(npc, entityplayer, cancelled -> { + cancellableUpdatePlayer(npc, entityplayer, cancelled -> { if (cancelled) { return; } @@ -88,7 +88,7 @@ public void updatePlayer(final ServerPlayer entityplayer) { super.updatePlayer(entityplayer); } - private void cancelledUpdatePlayer(final NPC npc, + private void cancellableUpdatePlayer(final NPC npc, final ServerPlayer entityplayer, final java.util.function.Consumer callback) { net.citizensnpcs.api.CitizensAPI.getScheduler().runEntityTask(entityplayer.getBukkitEntity(), () -> { From 1604d7b23e7a2d9bce766263333335471dc201cd Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Mon, 24 Nov 2025 18:41:06 +0100 Subject: [PATCH 45/50] Cache method handled --- .../nms/v1_20_R4/util/NMSImpl.java | 22 +++++++++---------- .../nms/v1_21_R5/util/NMSImpl.java | 22 +++++++++---------- .../nms/v1_21_R6/util/NMSImpl.java | 22 +++++++++---------- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java index 2d9fd52a0..d2dbd92c2 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java @@ -2801,6 +2801,8 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { private static final MethodHandle SIZE_FIELD_SETTER = NMS.getFirstSetter(Entity.class, EntityDimensions.class); private static MethodHandle SKULL_META_PROFILE; private static MethodHandle TEAM_FIELD; + private static final MethodHandle ENTITY_TRACKER_GETTER_FOLIA = NMS.getGetter(Entity.class, "tracker", false); + private static final MethodHandle ENTITY_TRACKER_SETTER_FOLIA = NMS.getSetter(Entity.class, "tracker", false); static { try { ENTITY_REGISTRY = new CustomEntityRegistry(BuiltInRegistries.ENTITY_TYPE); @@ -2813,22 +2815,18 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { private static TrackedEntity getTrackedEntityFolia(Entity entity) { try { - java.lang.reflect.Field field = Entity.class.getDeclaredField("tracker"); - field.setAccessible(true); - return (TrackedEntity) field.get(entity); - } catch (Throwable e) { - e.printStackTrace(System.err); + return (TrackedEntity) ENTITY_TRACKER_GETTER_FOLIA.invoke(entity); + } catch (Throwable t) { + t.printStackTrace(System.err); + return null; } - return null; } - private void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { + private static void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { try { - java.lang.reflect.Field field = Entity.class.getDeclaredField("tracker"); - field.setAccessible(true); - field.set(entity, trackedEntity); - } catch (Throwable e) { - e.printStackTrace(System.err); + ENTITY_TRACKER_SETTER_FOLIA.invoke(entity, trackedEntity); + } catch (Throwable t) { + t.printStackTrace(System.err); } } } diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java index ff98777d7..eb28f7fe9 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java @@ -2823,6 +2823,8 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { private static MethodHandle TEAM_FIELD; private static final Collection TRACKED_ENTITY_SETTERS = NMS.getSettersOfType(Entity.class, TrackedEntity.class); + private static final MethodHandle ENTITY_TRACKER_GETTER_FOLIA = NMS.getGetter(Entity.class, "trackedEntity", false); + private static final MethodHandle ENTITY_TRACKER_SETTER_FOLIA = NMS.getSetter(Entity.class, "trackedEntity", false); static { try { ENTITY_REGISTRY = new CustomEntityRegistry(BuiltInRegistries.ENTITY_TYPE); @@ -2836,22 +2838,18 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { private static TrackedEntity getTrackedEntityFolia(Entity entity) { try { - java.lang.reflect.Field field = Entity.class.getDeclaredField("trackedEntity"); - field.setAccessible(true); - return (TrackedEntity) field.get(entity); - } catch (Throwable e) { - e.printStackTrace(System.err); + return (TrackedEntity) ENTITY_TRACKER_GETTER_FOLIA.invoke(entity); + } catch (Throwable t) { + t.printStackTrace(System.err); + return null; } - return null; } - private void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { + private static void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { try { - java.lang.reflect.Field field = Entity.class.getDeclaredField("trackedEntity"); - field.setAccessible(true); - field.set(entity, trackedEntity); - } catch (Throwable e) { - e.printStackTrace(System.err); + ENTITY_TRACKER_SETTER_FOLIA.invoke(entity, trackedEntity); + } catch (Throwable t) { + t.printStackTrace(System.err); } } } diff --git a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java index 94d43cef1..36bf2d15c 100644 --- a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java +++ b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java @@ -2874,6 +2874,8 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { private static MethodHandle TEAM_FIELD; private static final Collection TRACKED_ENTITY_SETTERS = NMS.getSettersOfType(Entity.class, TrackedEntity.class); + private static final MethodHandle ENTITY_TRACKER_GETTER_FOLIA = NMS.getGetter(Entity.class, "trackedEntity", false); + private static final MethodHandle ENTITY_TRACKER_SETTER_FOLIA = NMS.getSetter(Entity.class, "trackedEntity", false); static { try { ENTITY_REGISTRY = new CustomEntityRegistry(BuiltInRegistries.ENTITY_TYPE); @@ -2887,22 +2889,18 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { private static TrackedEntity getTrackedEntityFolia(Entity entity) { try { - java.lang.reflect.Field field = Entity.class.getDeclaredField("trackedEntity"); - field.setAccessible(true); - return (TrackedEntity) field.get(entity); - } catch (Throwable e) { - e.printStackTrace(System.err); + return (TrackedEntity) ENTITY_TRACKER_GETTER_FOLIA.invoke(entity); + } catch (Throwable t) { + t.printStackTrace(System.err); + return null; } - return null; } - private void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { + private static void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { try { - java.lang.reflect.Field field = Entity.class.getDeclaredField("trackedEntity"); - field.setAccessible(true); - field.set(entity, trackedEntity); - } catch (Throwable e) { - e.printStackTrace(System.err); + ENTITY_TRACKER_SETTER_FOLIA.invoke(entity, trackedEntity); + } catch (Throwable t) { + t.printStackTrace(System.err); } } } From 60bbf5f388e9dc214a398014a9a99b406cd402dc Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 25 Nov 2025 05:17:09 +0100 Subject: [PATCH 46/50] Fix update upstream --- main/src/main/java/net/citizensnpcs/Citizens.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/src/main/java/net/citizensnpcs/Citizens.java b/main/src/main/java/net/citizensnpcs/Citizens.java index d1251a30c..361a28ee1 100644 --- a/main/src/main/java/net/citizensnpcs/Citizens.java +++ b/main/src/main/java/net/citizensnpcs/Citizens.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.UUID; +import net.citizensnpcs.api.util.schedulers.SchedulerTask; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.BlockCommandSender; @@ -142,7 +143,7 @@ public void updateInventoryTitle(Player player, InventoryViewAPI view, String ne private CitizensNPCRegistry npcRegistry; private boolean packetEventsEnabled = true; private PacketEventsListener packetEventsListener; - private BukkitTask playerUpdateTask; + private SchedulerTask playerUpdateTask; private boolean saveOnDisable = true; private NPCDataStore saves; private NPCSelector selector; From 469dcb9a02eaa936d8bdbc69c7c94acca307220d Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 25 Nov 2025 05:52:57 +0100 Subject: [PATCH 47/50] =?UTF-8?q?Utiliser=20snapTo=20seulement=20sur=20la?= =?UTF-8?q?=20m=C3=AAme=20region?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Petite explication: Sur Folia, un déplacement d'entité effectué via snapTo déclenche une mise à jour des sections qui necessite d'être exécutée sur le thread propriétaire de la région initiale. Cependant, il est possible que ce dernier ne soit pas dans la même region initiale, de ce fait Folia rejette la demande en générant l'erreur: Cannot move entity off-main qu'on se mette sur le thread de l'entité, ou le thread de la future localisation. De ce fait, je fais une vérification au préalable si c'est le même monde et que la region tické pour l'entité et la future localisation est le même, et si c'est le cas, snapTo fonctionne, sinon il faut lancer une teleportation async. De ce fait, quand tout est réuni, je garde le fonctionnement initial, sinon on est obligé de teleporter l'entité --- .../net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java | 10 ++++++++-- .../net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java | 9 ++++++++- .../net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java | 9 ++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java index d2dbd92c2..0e805c792 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java @@ -1618,9 +1618,15 @@ public void setKnockbackResistance(org.bukkit.entity.LivingEntity entity, double @Override public void setLocationDirectly(org.bukkit.entity.Entity entity, Location location) { - // Todo temp teleport if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { - net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); + if (location.getWorld() == entity.getWorld() + && CitizensAPI.getScheduler().isOnOwnerThread(entity) + && CitizensAPI.getScheduler().isOnOwnerThread(location)) { + getHandle(entity).moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), + location.getPitch()); + } else { + net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); + } return; } getHandle(entity).moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), diff --git a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java index eb28f7fe9..b74d9c769 100644 --- a/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java +++ b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java @@ -1611,7 +1611,14 @@ public void setHeadYaw(org.bukkit.entity.Entity entity, float yaw) { @Override public void setLocationDirectly(org.bukkit.entity.Entity entity, Location location) { if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { - net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); + if (location.getWorld() == entity.getWorld() + && CitizensAPI.getScheduler().isOnOwnerThread(entity) + && CitizensAPI.getScheduler().isOnOwnerThread(location)) { + getHandle(entity).snapTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), + location.getPitch()); + } else { + net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); + } return; } getHandle(entity).snapTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), diff --git a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java index 36bf2d15c..4d86ada2f 100644 --- a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java +++ b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java @@ -1647,7 +1647,14 @@ public void setHeadYaw(org.bukkit.entity.Entity entity, float yaw) { public void setLocationDirectly(org.bukkit.entity.Entity entity, Location location) { // Todo - temp teleport if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { - net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); + if (location.getWorld() == entity.getWorld() + && CitizensAPI.getScheduler().isOnOwnerThread(entity) + && CitizensAPI.getScheduler().isOnOwnerThread(location)) { + getHandle(entity).snapTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), + location.getPitch()); + } else { + net.citizensnpcs.api.util.SpigotUtil.teleportAsync(entity, location); + } return; } getHandle(entity).snapTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), From cff06825754ee42c1d56d3c12a7d856adb81bcc9 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Tue, 25 Nov 2025 05:54:15 +0100 Subject: [PATCH 48/50] enlever le commentaire todo --- .../main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java index 4d86ada2f..310a6eef4 100644 --- a/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java +++ b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java @@ -1645,7 +1645,6 @@ public void setHeadYaw(org.bukkit.entity.Entity entity, float yaw) { @Override public void setLocationDirectly(org.bukkit.entity.Entity entity, Location location) { - // Todo - temp teleport if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { if (location.getWorld() == entity.getWorld() && CitizensAPI.getScheduler().isOnOwnerThread(entity) From 4f48cc53bb19bb7aea69008f5462a53b035cf56d Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Fri, 28 Nov 2025 03:36:56 +0100 Subject: [PATCH 49/50] Remove Util#callPossiblySync --- .../main/java/net/citizensnpcs/util/Util.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/util/Util.java b/main/src/main/java/net/citizensnpcs/util/Util.java index c48a8ca44..86fac401c 100644 --- a/main/src/main/java/net/citizensnpcs/util/Util.java +++ b/main/src/main/java/net/citizensnpcs/util/Util.java @@ -81,25 +81,6 @@ public static boolean callPistonPushEvent(NPC npc) { return event.isCancelled(); } - public static T callPossiblySync(Callable callable, boolean sync) { - if (SpigotUtil.isFoliaServer()) { - throw new UnsupportedOperationException("Folia does not have a main thread, so it is not supported."); - } - if (!sync) { - try { - return callable.call(); - } catch (Exception e) { - e.printStackTrace(); - } - } - try { - return Bukkit.getScheduler().callSyncMethod(CitizensAPI.getPlugin(), callable).get(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - public static Vector callPushEvent(NPC npc, double x, double y, double z) { boolean allowed = npc == null || !npc.isProtected() || npc.data().has(NPC.Metadata.COLLIDABLE) && npc.data(). get(NPC.Metadata.COLLIDABLE); From 34eae058b8cd9bc73e76c22c11a36f657836c511 Mon Sep 17 00:00:00 2001 From: Euphyllia Bierque Date: Fri, 28 Nov 2025 03:49:26 +0100 Subject: [PATCH 50/50] =?UTF-8?q?Etre=20sur=20que=20NpcRegistry=20et=20Tra?= =?UTF-8?q?itFactory=20n'ai=20pas=20de=20probl=C3=A8me=20de=20concurrence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java | 2 +- .../main/java/net/citizensnpcs/npc/CitizensTraitFactory.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java index 335be9fce..1a597cefb 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java @@ -37,7 +37,7 @@ public class CitizensNPCRegistry implements NPCRegistry { private final String name; private final Int2ObjectOpenHashMap npcs = new Int2ObjectOpenHashMap<>(); private final NPCDataStore saves; - private final Map uniqueNPCs = Maps.newHashMap(); + private final Map uniqueNPCs = Maps.newConcurrentMap(); public CitizensNPCRegistry(NPCDataStore store) { this(store, ""); diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java b/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java index 669a34b18..aedf739a0 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java @@ -79,8 +79,8 @@ import net.citizensnpcs.util.NMS; public class CitizensTraitFactory implements TraitFactory { - private final List defaultTraits = Lists.newArrayList(); - private final Map registered = Maps.newHashMap(); + private final List defaultTraits = Lists.newCopyOnWriteArrayList(); + private final Map registered = Maps.newConcurrentMap(); public CitizensTraitFactory(Citizens plugin) { registerTrait(TraitInfo.create(Age.class));