diff --git a/main/src/main/java/net/citizensnpcs/Citizens.java b/main/src/main/java/net/citizensnpcs/Citizens.java index 8ca6d6c40..15a652b0a 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; @@ -147,7 +148,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; @@ -192,6 +193,9 @@ private void despawnNPCs(boolean save) { registry.saveToStore(); } } + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + if (!this.isEnabled()) return; + } registry.despawnNPCs(DespawnReason.RELOAD); } } @@ -463,7 +467,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(), 1) == null) { Messaging.severeTr(Messages.LOAD_TASK_NOT_SCHEDULED); Bukkit.getPluginManager().disablePlugin(this); } @@ -534,7 +538,7 @@ public void removeNamedNPCRegistry(String name) { } private void scheduleSaveTask(int delay) { - Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new CitizensSaveTask(), delay, delay); + CitizensAPI.getScheduler().runTaskTimer(new CitizensSaveTask(), delay, delay); } @Override diff --git a/main/src/main/java/net/citizensnpcs/EventListen.java b/main/src/main/java/net/citizensnpcs/EventListen.java index aef53d631..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; @@ -63,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; @@ -264,7 +264,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); } } @@ -419,7 +420,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); } @@ -535,7 +536,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); } } @@ -557,12 +558,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; @@ -611,7 +612,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); @@ -674,7 +675,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; @@ -778,7 +779,7 @@ public void onProjectileHit(ProjectileHitEvent event) { if (!(event.getEntity() instanceof FishHook)) return; NMS.removeHookIfNecessary((FishHook) event.getEntity()); - new BukkitRunnable() { + new SchedulerRunnable() { int n = 0; @Override @@ -789,7 +790,7 @@ public void run() { } NMS.removeHookIfNecessary((FishHook) event.getEntity()); } - }.runTaskTimer(plugin, 0, 1); + }.runEntityTaskTimer(plugin, event.getEntity(), null, 0, 1); } @EventHandler @@ -899,7 +900,7 @@ private void registerKnockbackEvent(Class kbc) { } } - private void registerMoveEvent(Class clazz) { +private void registerMoveEvent(Class clazz) { try { final HandlerList handlers = (HandlerList) clazz.getMethod("getHandlerList").invoke(null); final Method getEntity = clazz.getMethod("getEntity"); @@ -920,12 +921,12 @@ private void registerMoveEvent(Class clazz) { Bukkit.getPluginManager().callEvent(npcMoveEvent); if (npcMoveEvent.isCancelled()) { final Location eventFrom = npcMoveEvent.getFrom(); - Bukkit.getScheduler().runTaskLater(plugin, () -> entity.teleport(eventFrom), 1L); + CitizensAPI.getScheduler().runEntityTaskLater(entity, () -> SpigotUtil.teleportAsync(entity, eventFrom), 1L); return; } final Location eventTo = npcMoveEvent.getTo(); if (eventTo.getWorld() != to.getWorld() || eventTo.distance(to) > 0.001) { - Bukkit.getScheduler().runTaskLater(plugin, () -> entity.teleport(eventTo), 1L); + CitizensAPI.getScheduler().runEntityTaskLater(entity, () -> SpigotUtil.teleportAsync(entity, eventTo), 1L); } } catch (Throwable ex) { ex.printStackTrace(); @@ -1033,7 +1034,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(); } 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); diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index c322c3eef..1428d6cd2 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -900,7 +900,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(); } @@ -3186,14 +3186,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; } @@ -3208,7 +3208,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); @@ -3220,7 +3220,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)); } }); @@ -3599,7 +3599,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()); } @@ -3700,7 +3700,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); } diff --git a/main/src/main/java/net/citizensnpcs/npc/AbstractEntityController.java b/main/src/main/java/net/citizensnpcs/npc/AbstractEntityController.java index bad36de8f..42d35f02d 100644 --- a/main/src/main/java/net/citizensnpcs/npc/AbstractEntityController.java +++ b/main/src/main/java/net/citizensnpcs/npc/AbstractEntityController.java @@ -64,4 +64,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 3945b9e24..f677e4453 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; @@ -187,7 +188,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())); } @@ -333,8 +340,19 @@ 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; + 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) { if (!couldSpawn) { if (Messaging.isDebugging()) { Messaging.debug("Retrying spawn of", this, "later, SpawnReason." + reason + ". Was loaded", wasLoaded, @@ -436,12 +454,12 @@ public void accept(Runnable cancel) { postSpawn.accept(() -> { }); } else { - new BukkitRunnable() { + new SchedulerRunnable() { @Override public void run() { postSpawn.accept(this::cancel); } - }.runTaskTimer(CitizensAPI.getPlugin(), 0, 1); + }.runEntityTaskTimer(CitizensAPI.getPlugin(), getEntity(), null, 0, 1); } return true; } 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)); 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/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/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; 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()); } 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..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 BukkitRunnable() { + new SchedulerRunnable() { @Override public void run() { List visible = getNearbyNPCs(player, reset, false); @@ -300,11 +301,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 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 BukkitRunnable { + private static class NPCNavigationUpdater extends SchedulerRunnable { Queue queue = new ArrayDeque<>(20); @Override 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); } } 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; } 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; 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); } } diff --git a/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java b/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java index d66fe20e6..9b7eefd8f 100644 --- a/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java @@ -470,7 +470,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); @@ -492,7 +492,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); } } @@ -831,7 +831,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); } @@ -997,7 +997,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/PacketNPC.java b/main/src/main/java/net/citizensnpcs/trait/PacketNPC.java index 3ab2c9ea3..7773cb3e3 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); } @@ -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/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); } } 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++) { diff --git a/main/src/main/java/net/citizensnpcs/trait/SitTrait.java b/main/src/main/java/net/citizensnpcs/trait/SitTrait.java index 8b7e495e6..ce8511d88 100644 --- a/main/src/main/java/net/citizensnpcs/trait/SitTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/SitTrait.java @@ -40,7 +40,7 @@ public void onDespawn() { chair.destroy(); chair = null; if (requiresPassengerOffsetCorrection()) { - npc.getEntity().teleport(npc.getEntity().getLocation().clone().add(0, 0.3, 0)); + SpigotUtil.teleportAsync(npc.getEntity(), npc.getEntity().getLocation().clone().add(0, 0.3, 0)); } } } 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()? }); 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); }); 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())); } } 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); 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; } 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 diff --git a/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java b/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java index a3a25356f..1cc2ff02f 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; @@ -43,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; @@ -57,7 +62,7 @@ public void setForceLoaded(boolean b) { return; Chunk chunk = getChunk(); if (chunk != null) { - chunk.setForceLoaded(b); + CitizensAPI.getScheduler().runTask(() -> chunk.setForceLoaded(b)); } } diff --git a/main/src/main/java/net/citizensnpcs/util/NMS.java b/main/src/main/java/net/citizensnpcs/util/NMS.java index c850fad79..2938cf8a0 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); } @@ -832,19 +836,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) { @@ -1024,7 +1044,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/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java b/main/src/main/java/net/citizensnpcs/util/PlayerAnimation.java index 4cd11fc71..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 BukkitRunnable() { + new SchedulerRunnable() { @Override public void cancel() { super.cancel(); @@ -98,7 +99,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 +126,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 +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 BukkitRunnable() { + new SchedulerRunnable() { @Override public void run() { if (!NMS.isValid(player)) { @@ -150,7 +152,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; diff --git a/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java b/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java index be25416c0..95cc884b6 100644 --- a/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java +++ b/main/src/main/java/net/citizensnpcs/util/PlayerUpdateTask.java @@ -7,6 +7,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; @@ -20,9 +21,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 SchedulerRunnable { + private final java.util.Queue players = new java.util.concurrent.ConcurrentLinkedQueue<>(); + private final Set uuids = java.util.concurrent.ConcurrentHashMap.newKeySet(); @Override public void cancel() { @@ -87,7 +88,7 @@ public PlayerTick(Entity entity, Runnable tick) { @Override public void run() { - tick.run(); + net.citizensnpcs.api.CitizensAPI.getScheduler().runEntityTask(entity, tick); } } @@ -101,6 +102,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 Set PLAYERS_PENDING_REMOVE = new HashSet<>(); + 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<>(); } diff --git a/main/src/main/java/net/citizensnpcs/util/Util.java b/main/src/main/java/net/citizensnpcs/util/Util.java index f4cc9d1c1..86fac401c 100644 --- a/main/src/main/java/net/citizensnpcs/util/Util.java +++ b/main/src/main/java/net/citizensnpcs/util/Util.java @@ -81,22 +81,6 @@ public static boolean callPistonPushEvent(NPC npc) { return event.isCancelled(); } - public static T callPossiblySync(Callable callable, boolean sync) { - 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); @@ -474,7 +458,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(); diff --git a/main/src/main/resources/plugin.yml b/main/src/main/resources/plugin.yml index c466ea9f6..9979895fc 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 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_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..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,36 +77,47 @@ 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(); - } + cancellableUpdatePlayer(npc, entityplayer, cancelled -> { + if (cancelled) { + return; } - return false; - }, REQUIRES_SYNC); - - if (cancelled) - return; + super.updatePlayer(entityplayer); + }); + return; } super.updatePlayer(entityplayer); } + private void cancellableUpdatePlayer(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_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..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 @@ -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) @@ -1379,7 +1406,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 +1618,17 @@ public void setKnockbackResistance(org.bukkit.entity.LivingEntity entity, double @Override public void setLocationDirectly(org.bukkit.entity.Entity entity, Location location) { + if (net.citizensnpcs.api.util.SpigotUtil.isFoliaServer()) { + 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(), location.getPitch()); } @@ -2361,8 +2413,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(); @@ -2750,6 +2807,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); @@ -2759,4 +2818,21 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { Messaging.logTr(Messages.ERROR_GETTING_ID_MAPPING, e.getMessage()); } } + + private static TrackedEntity getTrackedEntityFolia(Entity entity) { + try { + return (TrackedEntity) ENTITY_TRACKER_GETTER_FOLIA.invoke(entity); + } catch (Throwable t) { + t.printStackTrace(System.err); + return null; + } + } + + private static void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { + try { + 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/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_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..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,36 +77,47 @@ 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(); - } + cancellableUpdatePlayer(npc, entityplayer, cancelled -> { + if (cancelled) { + return; } - return false; - }, REQUIRES_SYNC); - - if (cancelled) - return; + super.updatePlayer(entityplayer); + }); + return; } super.updatePlayer(entityplayer); } + private void cancellableUpdatePlayer(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/util/NMSImpl.java b/v1_21_R5/src/main/java/net/citizensnpcs/nms/v1_21_R5/util/NMSImpl.java index 2b7092e9f..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 @@ -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) @@ -1369,7 +1396,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(); @@ -1569,6 +1610,17 @@ 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()) { + 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(), location.getPitch()); } @@ -2254,8 +2306,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(); @@ -2773,6 +2830,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); @@ -2783,4 +2842,21 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { Messaging.logTr(Messages.ERROR_GETTING_ID_MAPPING, e.getMessage()); } } + + private static TrackedEntity getTrackedEntityFolia(Entity entity) { + try { + return (TrackedEntity) ENTITY_TRACKER_GETTER_FOLIA.invoke(entity); + } catch (Throwable t) { + t.printStackTrace(System.err); + return null; + } + } + + private static void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { + try { + 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/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; } 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..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,36 +77,47 @@ 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(); - } + cancellableUpdatePlayer(npc, entityplayer, cancelled -> { + if (cancelled) { + return; } - return false; - }, REQUIRES_SYNC); - - if (cancelled) - return; + super.updatePlayer(entityplayer); + }); + return; } super.updatePlayer(entityplayer); } + private void cancellableUpdatePlayer(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/NMSImpl.java b/v1_21_R6/src/main/java/net/citizensnpcs/nms/v1_21_R6/util/NMSImpl.java index 6091378e7..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 @@ -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) @@ -1404,7 +1431,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(); @@ -1604,6 +1645,17 @@ 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()) { + 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(), location.getPitch()); } @@ -2293,8 +2345,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(); @@ -2823,6 +2880,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); @@ -2833,4 +2892,21 @@ public static void updateMinecraftAIState(NPC npc, Mob entity) { Messaging.logTr(Messages.ERROR_GETTING_ID_MAPPING, e.getMessage()); } } + + private static TrackedEntity getTrackedEntityFolia(Entity entity) { + try { + return (TrackedEntity) ENTITY_TRACKER_GETTER_FOLIA.invoke(entity); + } catch (Throwable t) { + t.printStackTrace(System.err); + return null; + } + } + + private static void setTrackedEntityFolia(Entity entity, TrackedEntity trackedEntity) { + try { + ENTITY_TRACKER_SETTER_FOLIA.invoke(entity, trackedEntity); + } catch (Throwable t) { + t.printStackTrace(System.err); + } + } }