Skip to content

Commit 519ee16

Browse files
authored
Merge pull request #82 from sarhatabaot/offload-calculations
Offload calculations
2 parents d054d35 + b9043db commit 519ee16

File tree

12 files changed

+386
-192
lines changed

12 files changed

+386
-192
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
alias(libs.plugins.shadow)
66
alias(libs.plugins.plugin.yml)
77
}
8-
version = "4.3.12"
8+
version = "4.4.0"
99
description = "Limit entities in chunks."
1010

1111
dependencies {

src/main/java/com/cyprias/chunkspawnerlimiter/ChunkSpawnerLimiter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.cyprias.chunkspawnerlimiter.commands.CslCommand;
55
import com.cyprias.chunkspawnerlimiter.configs.impl.BlocksConfig;
66
import com.cyprias.chunkspawnerlimiter.configs.impl.CslConfig;
7+
import com.cyprias.chunkspawnerlimiter.inspection.entities.EntityChunkInspector;
78
import com.cyprias.chunkspawnerlimiter.listeners.EntityListener;
89
import com.cyprias.chunkspawnerlimiter.listeners.PlaceBlockListener;
910
import com.cyprias.chunkspawnerlimiter.listeners.WorldListener;
@@ -15,6 +16,7 @@
1516
import org.bukkit.plugin.java.JavaPlugin;
1617

1718
public class ChunkSpawnerLimiter extends JavaPlugin {
19+
private EntityChunkInspector entityChunkInspector;
1820
private CslConfig cslConfig;
1921

2022
private BlocksConfig blocksConfig;
@@ -27,6 +29,7 @@ public void onEnable() {
2729
ChatUtil.init(this);
2830
ChatUtil.logAndCheckArmorStandTickWarning();
2931

32+
this.entityChunkInspector = new EntityChunkInspector(this);
3033
registerListeners();
3134
PaperCommandManager paperCommandManager = new PaperCommandManager(this);
3235
paperCommandManager.enableUnstableAPI("help");
@@ -79,5 +82,7 @@ public CslConfig getCslConfig() {
7982
return cslConfig;
8083
}
8184

82-
85+
public EntityChunkInspector getChunkInspector() {
86+
return entityChunkInspector;
87+
}
8388
}

src/main/java/com/cyprias/chunkspawnerlimiter/configs/impl/BlocksConfig.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public class BlocksConfig extends ConfigFile<ChunkSpawnerLimiter> {
2222

2323
private Map<String, WorldLimits> worldLimits;
2424

25+
private int minLimitForCache;
26+
2527
public BlocksConfig(final @NotNull ChunkSpawnerLimiter plugin) {
2628
super(plugin, "", "blocks.yml", "");
2729
saveDefaultConfig();
@@ -38,8 +40,11 @@ public void initValues() {
3840
this.maxY = config.getInt("count.default.max-y", 256);
3941

4042
this.worldLimits = loadWorldLimits();
43+
44+
this.minLimitForCache = config.getInt("cache.min-limit-for-cache", 50);
4145
}
4246

47+
4348
private @NotNull Map<String, WorldLimits> loadWorldLimits() {
4449
final Map<String, WorldLimits> limits = new HashMap<>();
4550
final ConfigurationSection worldsSection = config.getConfigurationSection("count.worlds");
@@ -84,6 +89,10 @@ public boolean hasLimit(final Material material) {
8489
return materialLimits.containsKey(material);
8590
}
8691

92+
public int getMinLimitForCache() {
93+
return minLimitForCache;
94+
}
95+
8796
private @NotNull Map<Material, Integer> loadMaterialLimits() {
8897
final ConfigurationSection blockSection = config.getConfigurationSection("blocks");
8998
if (blockSection == null) {
@@ -151,4 +160,6 @@ public boolean isEnabled() {
151160
return enabled;
152161
}
153162

163+
164+
154165
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.cyprias.chunkspawnerlimiter.inspection.entities;
2+
3+
import org.bukkit.entity.Entity;
4+
5+
import java.util.List;
6+
7+
public class EntityChunkInspectionResult {
8+
private final List<Entity> entitiesToRemove;
9+
10+
public EntityChunkInspectionResult(List<Entity> entitiesToRemove) {
11+
this.entitiesToRemove = entitiesToRemove;
12+
}
13+
14+
public List<Entity> getEntitiesToRemove() {
15+
return entitiesToRemove;
16+
}
17+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package com.cyprias.chunkspawnerlimiter.inspection.entities;
2+
3+
4+
import com.cyprias.chunkspawnerlimiter.ChunkSpawnerLimiter;
5+
import com.cyprias.chunkspawnerlimiter.compare.MobGroupCompare;
6+
import com.cyprias.chunkspawnerlimiter.configs.impl.CslConfig;
7+
import com.cyprias.chunkspawnerlimiter.utils.ChatUtil;
8+
import com.cyprias.chunkspawnerlimiter.utils.Util;
9+
import org.bukkit.Chunk;
10+
import org.bukkit.Raid;
11+
import org.bukkit.entity.*;
12+
import org.bukkit.inventory.EntityEquipment;
13+
import org.bukkit.inventory.ItemStack;
14+
import org.bukkit.scheduler.BukkitRunnable;
15+
import org.jetbrains.annotations.NotNull;
16+
17+
import java.util.ArrayList;
18+
import java.util.HashMap;
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
public class EntityChunkInspector {
23+
private final ChunkSpawnerLimiter plugin;
24+
private final CslConfig config;
25+
26+
public EntityChunkInspector(@NotNull ChunkSpawnerLimiter plugin) {
27+
this.plugin = plugin;
28+
this.config = plugin.getCslConfig();
29+
}
30+
31+
/**
32+
* Checks the chunk for entities, removes entities if over the limit.
33+
*
34+
* @param chunk Chunk
35+
*/
36+
public void checkChunk(@NotNull Chunk chunk) {
37+
String worldName = chunk.getWorld().getName();
38+
if (config.isWorldNotAllowed(worldName)) {
39+
ChatUtil.debug("World %s is not allowed", worldName);
40+
return;
41+
}
42+
43+
Entity[] entities = chunk.getEntities();
44+
45+
// Offload calculations to an async task
46+
new BukkitRunnable() {
47+
@Override
48+
public void run() {
49+
// Perform calculations async
50+
Map<String, ArrayList<Entity>> types = addEntitiesByConfig(entities); // should be cached on load (already is)
51+
List<Entity> entitiesToRemove = new ArrayList<>();
52+
53+
for (Map.Entry<String, ArrayList<Entity>> entry : types.entrySet()) {
54+
String entityType = entry.getKey();
55+
List<Entity> entityList = entry.getValue();
56+
int limit = config.getEntityLimit(entityType);
57+
58+
if (entityList.size() > limit) {
59+
for (int i = entityList.size() - 1; i >= limit; i--) {
60+
Entity entity = entityList.get(i);
61+
if (!shouldPreserve(entity)) {
62+
entitiesToRemove.add(entity);
63+
}
64+
}
65+
}
66+
}
67+
68+
// Pass the results back to the main thread
69+
EntityChunkInspectionResult result = new EntityChunkInspectionResult(entitiesToRemove);
70+
new BukkitRunnable() {
71+
@Override
72+
public void run() {
73+
applyChanges(result); // Apply changes on the main thread
74+
}
75+
}.runTask(plugin);
76+
}
77+
}.runTaskAsynchronously(plugin);
78+
}
79+
80+
public void applyChanges(final @NotNull EntityChunkInspectionResult result) {
81+
for (Entity entity: result.getEntitiesToRemove()) {
82+
if (entity instanceof ArmorStand) {
83+
handleArmorStand(entity); // Handle Armor Stands
84+
continue;
85+
}
86+
87+
removeOrKillEntity(entity);
88+
}
89+
}
90+
91+
private void handleArmorStand(Entity entity) {
92+
if (!(entity instanceof ArmorStand)) {
93+
return;
94+
}
95+
96+
if (config.isDropItemsFromArmorStands()) {
97+
EntityEquipment entityEquipment = ((ArmorStand) entity).getEquipment();
98+
if (entityEquipment != null) {
99+
for (ItemStack itemStack : entityEquipment.getArmorContents()) {
100+
entity.getWorld().dropItemNaturally(entity.getLocation(), itemStack);
101+
}
102+
}
103+
}
104+
105+
if (Util.isArmorStandTickDisabled()) {
106+
ChatUtil.logArmorStandTickWarning();
107+
entity.remove();
108+
}
109+
}
110+
111+
private void removeOrKillEntity(Entity entity) {
112+
if (!config.isKillInsteadOfRemove() || !isKillable(entity)) {
113+
entity.remove();
114+
return;
115+
}
116+
117+
killEntity(entity);
118+
}
119+
120+
private void killEntity(final Entity entity) {
121+
((Damageable) entity).setHealth(0.0D);
122+
}
123+
124+
125+
public static boolean isKillable(final Entity entity) {
126+
return entity instanceof Damageable;
127+
}
128+
129+
130+
private @NotNull Map<String, ArrayList<Entity>> addEntitiesByConfig(Entity @NotNull [] entities) {
131+
HashMap<String, ArrayList<Entity>> modifiedTypes = new HashMap<>();
132+
for (int i = entities.length - 1; i >= 0; i--) {
133+
final Entity entity = entities[i];
134+
135+
String entityType = entity.getType().name();
136+
String entityMobGroup = MobGroupCompare.getMobGroup(entity);
137+
138+
addEntityIfHasLimit(modifiedTypes, entityType, entity);
139+
addEntityIfHasLimit(modifiedTypes, entityMobGroup, entity);
140+
}
141+
return modifiedTypes;
142+
}
143+
144+
private void addEntityIfHasLimit(Map<String, ArrayList<Entity>> modifiedTypes, String key, Entity entity) {
145+
if (config.hasEntityLimit(key)) {
146+
modifiedTypes.computeIfAbsent(key, k -> new ArrayList<>()).add(entity);
147+
}
148+
}
149+
150+
private boolean shouldPreserve(Entity entity) {
151+
return hasMetaData(entity) || hasCustomName(entity) || entity instanceof Player || isPartOfRaid(entity);
152+
}
153+
154+
private boolean hasCustomName(Entity entity) {
155+
return config.isPreserveNamedEntities() && entity.getCustomName() != null;
156+
}
157+
158+
private boolean hasMetaData(Entity entity) {
159+
for (String metadata : config.getIgnoreMetadata()) {
160+
if (entity.hasMetadata(metadata)) {
161+
return true;
162+
}
163+
}
164+
return false;
165+
}
166+
167+
private boolean isPartOfRaid(Entity entity) {
168+
if (!config.isPreserveRaidEntities()) {
169+
return false;
170+
}
171+
172+
if (entity instanceof Raider) {
173+
Raider raider = (Raider) entity;
174+
for (Raid raid : raider.getWorld().getRaids()) {
175+
boolean potentialMatch = raid.getRaiders().stream().anyMatch(r -> r.equals(raider));
176+
if (potentialMatch) {
177+
return true;
178+
}
179+
}
180+
}
181+
return false;
182+
}
183+
}

src/main/java/com/cyprias/chunkspawnerlimiter/tasks/InspectTask.java renamed to src/main/java/com/cyprias/chunkspawnerlimiter/inspection/entities/EntityInspectTask.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.cyprias.chunkspawnerlimiter.tasks;
1+
package com.cyprias.chunkspawnerlimiter.inspection.entities;
22

33
import com.cyprias.chunkspawnerlimiter.ChunkSpawnerLimiter;
44
import com.cyprias.chunkspawnerlimiter.utils.ChatUtil;
@@ -10,14 +10,14 @@
1010

1111
import java.lang.ref.WeakReference;
1212

13-
import static com.cyprias.chunkspawnerlimiter.listeners.WorldListener.checkChunk;
1413

1514
/**
1615
* A BukkitRunnable task that inspects a chunk for potential spawner issues.
1716
* The task checks if the chunk is loaded and then calls the checkChunk method.
1817
* If the chunk is not loaded, the task cancels itself.
1918
*/
20-
public class InspectTask extends BukkitRunnable {
19+
public class EntityInspectTask extends BukkitRunnable {
20+
private final EntityChunkInspector entityChunkInspector;
2121
/**
2222
* A WeakReference to the chunk being inspected.
2323
* This is used to prevent memory leaks when the chunk is garbage collected.
@@ -51,16 +51,17 @@ public void run() {
5151
return;
5252
}
5353

54-
checkChunk(chunk);
54+
entityChunkInspector.checkChunk(chunk);
5555
}
5656

5757
/**
5858
* Constructs a new InspectTask for the given chunk.
5959
*
6060
* @param chunk The chunk to be inspected
6161
*/
62-
public InspectTask(final Chunk chunk) {
62+
public EntityInspectTask(final Chunk chunk, final EntityChunkInspector entityChunkInspector) {
6363
this.refChunk = new WeakReference<>(chunk);
64+
this.entityChunkInspector = entityChunkInspector;
6465
}
6566

6667
/**

src/main/java/com/cyprias/chunkspawnerlimiter/listeners/EntityListener.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.cyprias.chunkspawnerlimiter.listeners;
22

33
import com.cyprias.chunkspawnerlimiter.ChunkSpawnerLimiter;
4+
import com.cyprias.chunkspawnerlimiter.inspection.entities.EntityChunkInspector;
45
import com.cyprias.chunkspawnerlimiter.utils.ChatUtil;
56
import com.cyprias.chunkspawnerlimiter.messages.Debug;
67
import org.bukkit.Chunk;
@@ -18,9 +19,11 @@
1819
*/
1920
public class EntityListener implements Listener {
2021
private final CslConfig config;
22+
private final EntityChunkInspector entityChunkInspector;
2123

2224
public EntityListener(final ChunkSpawnerLimiter plugin) {
2325
this.config = plugin.getCslConfig();
26+
this.entityChunkInspector = plugin.getChunkInspector();
2427
}
2528

2629
@EventHandler
@@ -37,7 +40,7 @@ public void onCreatureSpawnEvent(@NotNull CreatureSpawnEvent event) {
3740
}
3841

3942
Chunk chunk = event.getLocation().getChunk();
40-
WorldListener.checkChunk(chunk);
43+
entityChunkInspector.checkChunk(chunk);
4144
checkSurroundings(chunk);
4245
}
4346

@@ -51,7 +54,7 @@ public void onVehicleCreateEvent(@NotNull VehicleCreateEvent event) {
5154
Chunk chunk = event.getVehicle().getLocation().getChunk();
5255

5356
ChatUtil.debug(Debug.VEHICLE_CREATE_EVENT, chunk.getX(), chunk.getZ());
54-
WorldListener.checkChunk(chunk);
57+
entityChunkInspector.checkChunk(chunk);
5558
checkSurroundings(chunk);
5659
}
5760

@@ -64,7 +67,7 @@ public void onEntitySpawnEvent(@NotNull EntitySpawnEvent event) {
6467
Chunk chunk = event.getEntity().getLocation().getChunk();
6568

6669
ChatUtil.debug("Entity Spawn Event: %s, %dx, %dz ", event.getEntity().getType().name(), chunk.getX(), chunk.getZ());
67-
WorldListener.checkChunk(chunk);
70+
entityChunkInspector.checkChunk(chunk);
6871
checkSurroundings(chunk);
6972
}
7073

@@ -74,7 +77,7 @@ private void checkSurroundings(Chunk chunk) {
7477
if (surrounding > 0) {
7578
for (int x = chunk.getX() + surrounding; x >= (chunk.getX() - surrounding); x--) {
7679
for (int z = chunk.getZ() + surrounding; z >= (chunk.getZ() - surrounding); z--) {
77-
WorldListener.checkChunk(chunk.getWorld().getChunkAt(x, z));
80+
entityChunkInspector.checkChunk(chunk.getWorld().getChunkAt(x, z));
7881
}
7982
}
8083
}

0 commit comments

Comments
 (0)