Skip to content

Commit 4e90e72

Browse files
committed
Improved the npc system
1 parent 949e054 commit 4e90e72

5 files changed

Lines changed: 137 additions & 55 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>me.waterarchery</groupId>
88
<artifactId>LitLibs</artifactId>
9-
<version>1.1.20</version>
9+
<version>1.1.21</version>
1010
<packaging>jar</packaging>
1111

1212
<name>LitLibs</name>

src/main/java/me/waterarchery/litlibs/LitLibsPlugin.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.github.retrooper.packetevents.event.PacketListenerPriority;
55
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
66
import lombok.Getter;
7+
import me.waterarchery.litlibs.listeners.ChunkListeners;
78
import me.waterarchery.litlibs.listeners.PacketListeners;
89
import me.waterarchery.litlibs.listeners.PluginDisabledListener;
910
import me.waterarchery.litlibs.logger.LogSeverity;
@@ -41,7 +42,9 @@ public void onEnable() {
4142

4243
versionHandler = VersionHandler.getInstance();
4344
litLogger.log("LitLibs enabled version &av" + version, LogSeverity.NORMAL);
45+
4446
getServer().getPluginManager().registerEvents(new PluginDisabledListener(), this);
47+
getServer().getPluginManager().registerEvents(new ChunkListeners(), this);
4548
}
4649

4750
@Override

src/main/java/me/waterarchery/litlibs/handlers/NPCHandler.java

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@
33
import com.google.common.util.concurrent.ThreadFactoryBuilder;
44
import lombok.Getter;
55
import lombok.Setter;
6+
import me.waterarchery.litlibs.LitLibsPlugin;
67
import me.waterarchery.litlibs.impl.npc.NPC;
8+
import me.waterarchery.litlibs.utils.ChunkUtils;
79
import org.bukkit.Bukkit;
810
import org.bukkit.ChatColor;
11+
import org.bukkit.Location;
12+
import org.bukkit.World;
13+
import org.bukkit.entity.Player;
14+
import org.bukkit.scheduler.BukkitTask;
915
import org.bukkit.scoreboard.Scoreboard;
1016
import org.bukkit.scoreboard.Team;
1117

1218
import java.util.ArrayList;
1319
import java.util.List;
20+
import java.util.Objects;
1421
import java.util.UUID;
1522
import java.util.concurrent.ExecutorService;
1623
import java.util.concurrent.Executors;
@@ -24,6 +31,7 @@ public class NPCHandler {
2431
private final List<NPC> npcs = new ArrayList<>();
2532
private final ThreadFactory namedThreadFactory;
2633
private final ExecutorService executor;
34+
private BukkitTask updateTask;
2735

2836
public static NPCHandler getInstance() {
2937
if (instance == null) instance = new NPCHandler();
@@ -33,7 +41,57 @@ public static NPCHandler getInstance() {
3341

3442
private NPCHandler() {
3543
namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("litlibs-%d").build();
36-
executor = Executors.newCachedThreadPool(namedThreadFactory);
44+
executor = Executors.newSingleThreadExecutor(namedThreadFactory);
45+
46+
startUpdateTask();
47+
}
48+
49+
public void startUpdateTask() {
50+
if (updateTask != null) updateTask.cancel();
51+
52+
updateTask = Bukkit.getScheduler().runTaskTimerAsynchronously(LitLibsPlugin.getInstance(), () -> {
53+
for (NPC npc : new ArrayList<>(npcs)) {
54+
if (npc.isDespawned()) continue;
55+
56+
Location location = npc.getLocation();
57+
58+
if (!location.isWorldLoaded()) continue;
59+
if (!ChunkUtils.isChunkLoaded(location.getWorld(), location.getBlockX() / 16, location.getBlockZ() / 16)) continue;
60+
61+
World world = location.getWorld();
62+
if (world == null) continue;
63+
64+
List<UUID> clone = new ArrayList<>(npc.getSeeingPlayers());
65+
clone.forEach(playerUUID -> {
66+
Player player = Bukkit.getPlayer(playerUUID);
67+
if (player == null) {
68+
npc.getSeeingPlayers().remove(playerUUID);
69+
return;
70+
}
71+
72+
Location playerLocation = player.getLocation();
73+
if (!Objects.equals(playerLocation.getWorld(), world)) npc.despawn(player, false);
74+
});
75+
76+
List<UUID> newSeeingList = new ArrayList<>();
77+
for (Player player : Bukkit.getOnlinePlayers()) {
78+
if (player.getName().startsWith("Loader-")) continue; // Wild Loaders
79+
if (!player.getLocation().getWorld().equals(location.getWorld())) continue;
80+
if (player.getLocation().distance(location) > 31) {
81+
if (npc.getSeeingPlayers().contains(player.getUniqueId())) npc.despawn(player, false);
82+
continue;
83+
}
84+
85+
newSeeingList.add(player.getUniqueId());
86+
87+
if (!npc.getSeeingPlayers().contains(player.getUniqueId())) {
88+
Bukkit.getScheduler().runTaskLaterAsynchronously(LitLibsPlugin.getInstance(), () -> npc.spawn(player), 5);
89+
}
90+
}
91+
92+
npc.setSeeingPlayers(new ArrayList<>(newSeeingList));
93+
}
94+
}, 0, 10);
3795
}
3896

3997
public void deleteNPC(UUID uuid) {

src/main/java/me/waterarchery/litlibs/impl/npc/NPC.java

Lines changed: 20 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import me.waterarchery.litlibs.version.VersionHandler;
1919
import org.bukkit.*;
2020
import org.bukkit.entity.Player;
21-
import org.bukkit.scheduler.BukkitRunnable;
22-
import org.bukkit.scheduler.BukkitTask;
2321
import org.bukkit.scoreboard.Team;
2422

2523
import java.util.ArrayList;
@@ -44,9 +42,9 @@ public abstract class NPC {
4442
protected double z;
4543
protected float yaw;
4644
protected float pitch;
47-
protected BukkitTask updateSeeingTask;
4845
protected Consumer<Player> onClickAction;
4946
protected final NPCHandler npcHandler;
47+
protected boolean despawned;
5048

5149
public NPC(String name, String worldName, double x, double y, double z, EntityType entityType, Consumer<Player> onClickAction) {
5250
this.uuid = UUID.randomUUID();
@@ -63,68 +61,22 @@ public NPC(String name, String worldName, double x, double y, double z, EntityTy
6361
this.equipments = new ArrayList<>();
6462
this.entityType = entityType;
6563

64+
despawned = isChunkLoaded();
6665
npcHandler = NPCHandler.getInstance();
6766

6867
VersionHandler versionHandler = VersionHandler.getInstance();
6968
oldVersion = versionHandler.isServerOlderThan(Version.v1_17);
7069

7170
npcHandler.getNpcs().add(this);
72-
startSeeingTask();
7371
}
7472

7573
public void execute(Player player) {
7674
onClickAction.accept(player);
7775
}
7876

79-
public void startSeeingTask() {
80-
if (updateSeeingTask != null) {
81-
seeingPlayers.clear();
82-
updateSeeingTask.cancel();
83-
}
84-
85-
updateSeeingTask = new BukkitRunnable() {
86-
@Override
87-
public void run() {
88-
Location location = getLocation();
89-
90-
if (!location.isWorldLoaded()) return;
91-
if (!ChunkUtils.isChunkLoaded(location.getWorld(), location.getBlockX() / 16, location.getBlockZ() / 16)) return;
92-
93-
World world = location.getWorld();
94-
if (world == null) return;
95-
96-
List<UUID> clone = new ArrayList<>(seeingPlayers);
97-
clone.forEach(playerUUID -> {
98-
Player player = Bukkit.getPlayer(playerUUID);
99-
if (player == null) {
100-
seeingPlayers.remove(playerUUID);
101-
return;
102-
}
103-
104-
Location playerLocation = player.getLocation();
105-
if (playerLocation.getWorld() != world) despawn(player, false);
106-
else if (playerLocation.distance(location) > 32) despawn(player, false);
107-
});
108-
109-
List<UUID> newSeeingList = new ArrayList<>();
110-
world.getNearbyEntities(location, 32, 32, 32, (e) -> e.getType() == org.bukkit.entity.EntityType.PLAYER)
111-
.forEach(player -> {
112-
if (player.getLocation().distance(location) > 31) return;
113-
if (player.getName().startsWith("Loader-")) return; // Wild Loaders
114-
115-
newSeeingList.add(player.getUniqueId());
116-
117-
if (!seeingPlayers.contains(player.getUniqueId())) {
118-
Bukkit.getScheduler().runTaskLaterAsynchronously(LitLibsPlugin.getInstance(), () -> spawn((Player) player), 5);
119-
}
120-
});
121-
122-
seeingPlayers = new ArrayList<>(newSeeingList);
123-
}
124-
}.runTaskTimer(LitLibsPlugin.getInstance(), 0, 10);
125-
}
126-
12777
public void spawn(Player player) {
78+
despawned = false;
79+
12880
Location location = getLocation();
12981
com.github.retrooper.packetevents.protocol.world.Location spawnLocation = SpigotConversionUtil.fromBukkitLocation(location);
13082

@@ -172,7 +124,6 @@ public void updateRotation() {
172124
}
173125

174126
public void despawn() {
175-
updateSeeingTask.cancel();
176127
seeingPlayers.clear();
177128

178129
Bukkit.getOnlinePlayers().forEach(player -> despawn(player, true));
@@ -247,6 +198,22 @@ private void sendPacketSync(PacketWrapper<?> packet, Player player) {
247198
}
248199
}
249200

201+
public boolean isChunkLoaded() {
202+
World world = Bukkit.getWorld(worldName);
203+
if (world == null) return false;
204+
205+
206+
return ChunkUtils.isChunkLoaded(getLocation());
207+
}
208+
209+
public int getChunkX() {
210+
return getLocation().getBlockX() >> 4;
211+
}
212+
213+
public int getChunkZ() {
214+
return getLocation().getBlockZ() >> 4;
215+
}
216+
250217
public Location getLocation() {
251218
return new Location(Bukkit.getWorld(worldName), x, y, z);
252219
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package me.waterarchery.litlibs.listeners;
2+
3+
import me.waterarchery.litlibs.LitLibsPlugin;
4+
import me.waterarchery.litlibs.handlers.NPCHandler;
5+
import me.waterarchery.litlibs.impl.npc.NPC;
6+
import org.bukkit.Bukkit;
7+
import org.bukkit.Chunk;
8+
import org.bukkit.World;
9+
import org.bukkit.event.EventHandler;
10+
import org.bukkit.event.EventPriority;
11+
import org.bukkit.event.Listener;
12+
import org.bukkit.event.world.ChunkLoadEvent;
13+
import org.bukkit.event.world.ChunkUnloadEvent;
14+
15+
public class ChunkListeners implements Listener {
16+
17+
@EventHandler (ignoreCancelled = true, priority = EventPriority.HIGHEST)
18+
public void onNpcLoad(ChunkLoadEvent event) {
19+
Chunk chunk = event.getChunk();
20+
int x = chunk.getX();
21+
int z = chunk.getZ();
22+
String world = chunk.getWorld().getName();
23+
24+
NPCHandler npcHandler = NPCHandler.getInstance();
25+
26+
for (NPC npc : npcHandler.getNpcs()) {
27+
if (world.equalsIgnoreCase(npc.getWorldName()) && npc.getChunkX() == x && npc.getChunkZ() == z) {
28+
npc.setDespawned(false);
29+
}
30+
}
31+
}
32+
33+
@EventHandler (ignoreCancelled = true, priority = EventPriority.HIGHEST)
34+
public void onChunkUnload(ChunkUnloadEvent event) {
35+
Chunk chunk = event.getChunk();
36+
int x = chunk.getX();
37+
int z = chunk.getZ();
38+
String worldName = chunk.getWorld().getName();
39+
40+
Bukkit.getScheduler().runTaskLaterAsynchronously(LitLibsPlugin.getInstance(), () -> {
41+
World world = Bukkit.getWorld(worldName);
42+
if (world != null && world.isChunkLoaded(x, z)) return;
43+
44+
NPCHandler npcHandler = NPCHandler.getInstance();
45+
46+
for (NPC npc : npcHandler.getNpcs()) {
47+
if (worldName.equalsIgnoreCase(npc.getWorldName()) && npc.getChunkX() == x && npc.getChunkZ() == z) {
48+
npc.despawn();
49+
}
50+
}
51+
}, 10);
52+
}
53+
54+
}

0 commit comments

Comments
 (0)