diff --git a/src/main/java/net/thecommandcraft/vanishpp/hooks/ProtocolLibManager.java b/src/main/java/net/thecommandcraft/vanishpp/hooks/ProtocolLibManager.java index 82cb5a3..9588b74 100644 --- a/src/main/java/net/thecommandcraft/vanishpp/hooks/ProtocolLibManager.java +++ b/src/main/java/net/thecommandcraft/vanishpp/hooks/ProtocolLibManager.java @@ -236,6 +236,15 @@ public void onPacketSending(PacketEvent event) { // Actions: 0 (Create), 3 (Add Members), 4 (Remove Members) if (action == 0 || action == 3 || action == 4) { + // For the vanish team, non-staff clients never receive ADD packets for + // vanished players, so they must also never receive REMOVE packets — + // otherwise MC crashes with "Player is not on any team" when unvanishing. + String teamName = packet.getStrings().read(0); + if ("Vanishpp_Vanished".equals(teamName) && (action == 3 || action == 4)) { + event.setCancelled(true); + return; + } + Collection players = packet.getSpecificModifier(Collection.class).read(0); if (players != null) { List scrubbed = new ArrayList<>(); diff --git a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/Vanishpp.java b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/Vanishpp.java index 9f24c53..85fe25c 100644 --- a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/Vanishpp.java +++ b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/Vanishpp.java @@ -970,10 +970,15 @@ public void applyVanishEffects(Player player) { // ALWAYS clear existing mob targets when vanishing (regardless of mob_targeting rule) // The rule only controls whether new targets can be acquired AFTER vanishing + // Each mob's target access must run on its owning region's thread (Folia safety) for (Entity entity : player.getWorld().getEntities()) { - if (entity instanceof Mob mob && player.equals(mob.getTarget())) { - mob.setTarget(null); - mob.getPathfinder().stopPathfinding(); + if (entity instanceof Mob mob) { + vanishScheduler.runEntity(mob, () -> { + if (player.equals(mob.getTarget())) { + mob.setTarget(null); + mob.getPathfinder().stopPathfinding(); + } + }, null); } } diff --git a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/hooks/ProtocolLibManager.java b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/hooks/ProtocolLibManager.java index 8084761..f512997 100644 --- a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/hooks/ProtocolLibManager.java +++ b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/hooks/ProtocolLibManager.java @@ -233,6 +233,15 @@ public void onPacketSending(PacketEvent event) { // Actions: 0 (Create), 3 (Add Members), 4 (Remove Members) if (action == 0 || action == 3 || action == 4) { + // For the vanish team, non-staff clients never receive ADD packets for + // vanished players, so they must also never receive REMOVE packets — + // otherwise MC crashes with "Player is not on any team" when unvanishing. + String teamName = packet.getStrings().read(0); + if ("Vanishpp_Vanished".equals(teamName) && (action == 3 || action == 4)) { + event.setCancelled(true); + return; + } + Collection players = packet.getSpecificModifier(Collection.class).read(0); if (players != null) { List scrubbed = new ArrayList<>(); diff --git a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/listeners/MobAiManager.java b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/listeners/MobAiManager.java index 9e90302..1fbf539 100644 --- a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/listeners/MobAiManager.java +++ b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/listeners/MobAiManager.java @@ -26,21 +26,21 @@ public void register() { private void sweepMobTargets() { for (Player p : Bukkit.getOnlinePlayers()) { if (!plugin.isVanished(p)) continue; - - // Only enforce clearing if mob_targeting rule is OFF if (plugin.getRuleManager().getRule(p, RuleManager.MOB_TARGETING)) continue; - // Check all nearby mobs and clear any that have the vanished player as target - for (Entity entity : p.getNearbyEntities(128, 128, 128)) { - if (!(entity instanceof Mob mob)) continue; - - // Clear target if aimed at this vanished player - if (p.equals(mob.getTarget())) { - mob.setTarget(null); - try { - mob.getPathfinder().stopPathfinding(); - } catch (Throwable ignored) {} - } + // getNearbyEntities requires the owning region thread — dispatch per entity. + plugin.getVanishScheduler().runEntity(p, () -> sweepForPlayer(p), null); + } + } + + private void sweepForPlayer(Player p) { + for (Entity entity : p.getNearbyEntities(128, 128, 128)) { + if (!(entity instanceof Mob mob)) continue; + if (p.equals(mob.getTarget())) { + mob.setTarget(null); + try { + mob.getPathfinder().stopPathfinding(); + } catch (Throwable ignored) {} } } } diff --git a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/storage/SqlStorage.java b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/storage/SqlStorage.java index eb21307..cf6ae4d 100644 --- a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/storage/SqlStorage.java +++ b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/storage/SqlStorage.java @@ -569,6 +569,25 @@ public VanishStats getStats(UUID uuid) { return VanishStats.empty(); } + @Override + public void setStats(UUID uuid, VanishStats stats) { + String q = type.equals("postgresql") + ? "INSERT INTO vpp_stats (uuid,total_ms,vanish_count,longest_ms) VALUES(?,?,?,?)" + + " ON CONFLICT (uuid) DO UPDATE SET total_ms=EXCLUDED.total_ms," + + " vanish_count=EXCLUDED.vanish_count, longest_ms=EXCLUDED.longest_ms" + : "INSERT INTO vpp_stats (uuid,total_ms,vanish_count,longest_ms) VALUES(?,?,?,?)" + + " ON DUPLICATE KEY UPDATE total_ms=VALUES(total_ms)," + + " vanish_count=VALUES(vanish_count), longest_ms=VALUES(longest_ms)"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(q)) { + ps.setString(1, uuid.toString()); + ps.setLong(2, stats.getTotalVanishTimeMs()); + ps.setInt(3, stats.getVanishCount()); + ps.setLong(4, stats.getLongestSessionMs()); + ps.executeUpdate(); + } catch (SQLException e) { handleDatabaseError(e); } + } + @Override public void recordVanishSession(UUID uuid, long durationMs) { if (durationMs <= 0) return; diff --git a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/storage/YamlStorage.java b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/storage/YamlStorage.java index de56453..4b930fc 100644 --- a/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/storage/YamlStorage.java +++ b/vanishpp-paper/src/main/java/net/thecommandcraft/vanishpp/storage/YamlStorage.java @@ -291,6 +291,15 @@ public VanishStats getStats(UUID uuid) { return new VanishStats(total, count, longest); } + @Override + public void setStats(UUID uuid, VanishStats stats) { + String base = "stats." + uuid + "."; + config.set(base + "total-ms", stats.getTotalVanishTimeMs()); + config.set(base + "count", stats.getVanishCount()); + config.set(base + "longest-ms", stats.getLongestSessionMs()); + save(); + } + @Override public void recordVanishSession(UUID uuid, long durationMs) { if (durationMs <= 0) return;