improve chunk limit

This commit is contained in:
xGinko 2023-10-04 14:11:20 +02:00
parent 748cb08c9c
commit 0aa37dea8e
2 changed files with 78 additions and 48 deletions

View File

@ -26,9 +26,10 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener, Ru
private final Server server; private final Server server;
private final VillagerCache villagerCache; private final VillagerCache villagerCache;
private final List<Villager.Profession> removal_priority = new ArrayList<>(16); private final List<Villager.Profession> non_optimized_removal_priority = new ArrayList<>(16);
private final List<Villager.Profession> optimized_removal_priority = new ArrayList<>(16);
private final long check_period; private final long check_period;
private final int max_unoptimized_per_chunk, max_optimized_per_chunk; private final int non_optimized_max_per_chunk, optimized_max_per_chunk;
private final boolean log_enabled; private final boolean log_enabled;
protected VillagerChunkLimit() { protected VillagerChunkLimit() {
@ -40,16 +41,14 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener, Ru
Checks chunks for too many villagers and removes excess villagers based on priority.\s Checks chunks for too many villagers and removes excess villagers based on priority.\s
Naturally, optimized villagers will be picked last since they don't affect performance\s Naturally, optimized villagers will be picked last since they don't affect performance\s
as much as unoptimized villagers."""); as much as unoptimized villagers.""");
this.max_unoptimized_per_chunk = config.getInt("villager-chunk-limit.max-unoptimized-per-chunk", 30,
"The maximum amount of unoptimized villagers per chunk.");
this.max_optimized_per_chunk = config.getInt("villager-chunk-limit.max-optimized-per-chunk", 20,
"The maximum amount of optimized villagers per chunk.");
this.check_period = config.getInt("villager-chunk-limit.check-period-in-ticks", 600, """ this.check_period = config.getInt("villager-chunk-limit.check-period-in-ticks", 600, """
Check all loaded chunks every X ticks. 1 second = 20 ticks\s Check all loaded chunks every X ticks. 1 second = 20 ticks\s
A shorter delay in between checks is more efficient but is also more resource intense.\s A shorter delay in between checks is more efficient but is also more resource intense.\s
A larger delay is less resource intense but could become inefficient."""); A larger delay is less resource intense but could become inefficient.""");
this.log_enabled = config.getBoolean("villager-chunk-limit.log-removals", false); this.log_enabled = config.getBoolean("villager-chunk-limit.log-removals", false);
config.getList("villager-chunk-limit.removal-priority", List.of( this.non_optimized_max_per_chunk = config.getInt("villager-chunk-limit.unoptimized.max-per-chunk", 20,
"The maximum amount of unoptimized villagers per chunk.");
config.getList("villager-chunk-limit.unoptimized.removal-priority", List.of(
"NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER", "NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER",
"FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN" "FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN"
), """ ), """
@ -58,9 +57,24 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener, Ru
).forEach(configuredProfession -> { ).forEach(configuredProfession -> {
try { try {
Villager.Profession profession = Villager.Profession.valueOf(configuredProfession); Villager.Profession profession = Villager.Profession.valueOf(configuredProfession);
this.removal_priority.add(profession); this.non_optimized_removal_priority.add(profession);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
LogUtil.moduleLog(Level.WARNING, "villager-chunk-limit", LogUtil.moduleLog(Level.WARNING, "villager-chunk-limit.unoptimized",
"Villager profession '"+configuredProfession+"' not recognized. " +
"Make sure you're using the correct profession enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html.");
}
});
this.optimized_max_per_chunk = config.getInt("villager-chunk-limit.optimized.max-per-chunk", 60,
"The maximum amount of optimized villagers per chunk.");
config.getList("villager-chunk-limit.optimized.removal-priority", List.of(
"NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER",
"FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN"
)).forEach(configuredProfession -> {
try {
Villager.Profession profession = Villager.Profession.valueOf(configuredProfession);
this.optimized_removal_priority.add(profession);
} catch (IllegalArgumentException e) {
LogUtil.moduleLog(Level.WARNING, "villager-chunk-limit.optimized",
"Villager profession '"+configuredProfession+"' not recognized. " + "Villager profession '"+configuredProfession+"' not recognized. " +
"Make sure you're using the correct profession enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html."); "Make sure you're using the correct profession enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html.");
} }
@ -83,7 +97,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener, Ru
public void run() { public void run() {
for (World world : server.getWorlds()) { for (World world : server.getWorlds()) {
for (Chunk chunk : world.getLoadedChunks()) { for (Chunk chunk : world.getLoadedChunks()) {
this.checkVillagers(chunk); this.manageVillagerCount(chunk);
} }
} }
} }
@ -92,7 +106,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener, Ru
private void onCreatureSpawn(CreatureSpawnEvent event) { private void onCreatureSpawn(CreatureSpawnEvent event) {
Entity spawned = event.getEntity(); Entity spawned = event.getEntity();
if (spawned.getType().equals(EntityType.VILLAGER)) { if (spawned.getType().equals(EntityType.VILLAGER)) {
this.checkVillagers(spawned.getChunk()); this.manageVillagerCount(spawned.getChunk());
} }
} }
@ -100,11 +114,11 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener, Ru
private void onInteract(PlayerInteractEntityEvent event) { private void onInteract(PlayerInteractEntityEvent event) {
Entity clicked = event.getRightClicked(); Entity clicked = event.getRightClicked();
if (clicked.getType().equals(EntityType.VILLAGER)) { if (clicked.getType().equals(EntityType.VILLAGER)) {
this.checkVillagers(clicked.getChunk()); this.manageVillagerCount(clicked.getChunk());
} }
} }
private void checkVillagers(@NotNull Chunk chunk) { private void manageVillagerCount(@NotNull Chunk chunk) {
// Collect all optimized and unoptimized villagers in that chunk // Collect all optimized and unoptimized villagers in that chunk
List<Villager> optimized_villagers = new ArrayList<>(); List<Villager> optimized_villagers = new ArrayList<>();
List<Villager> not_optimized_villagers = new ArrayList<>(); List<Villager> not_optimized_villagers = new ArrayList<>();
@ -121,10 +135,13 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener, Ru
} }
// Check if there are more unoptimized villagers in that chunk than allowed // Check if there are more unoptimized villagers in that chunk than allowed
final int not_optimized_villagers_too_many = not_optimized_villagers.size() - max_unoptimized_per_chunk; final int not_optimized_villagers_too_many = not_optimized_villagers.size() - non_optimized_max_per_chunk;
if (not_optimized_villagers_too_many > 0) { if (not_optimized_villagers_too_many > 0) {
// Sort villagers by profession priority // Sort villagers by profession priority
not_optimized_villagers.sort(Comparator.comparingInt(this::getProfessionPriority)); not_optimized_villagers.sort(Comparator.comparingInt(villager -> {
final Villager.Profession profession = villager.getProfession();
return non_optimized_removal_priority.contains(profession) ? non_optimized_removal_priority.indexOf(profession) : Integer.MAX_VALUE;
}));
// Remove prioritized villagers that are too many // Remove prioritized villagers that are too many
for (int i = 0; i < not_optimized_villagers_too_many; i++) { for (int i = 0; i < not_optimized_villagers_too_many; i++) {
Villager villager = not_optimized_villagers.get(i); Villager villager = not_optimized_villagers.get(i);
@ -136,10 +153,13 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener, Ru
} }
// Check if there are more optimized villagers in that chunk than allowed // Check if there are more optimized villagers in that chunk than allowed
final int optimized_villagers_too_many = optimized_villagers.size() - max_optimized_per_chunk; final int optimized_villagers_too_many = optimized_villagers.size() - optimized_max_per_chunk;
if (optimized_villagers_too_many > 0) { if (optimized_villagers_too_many > 0) {
// Sort villagers by profession priority // Sort villagers by profession priority
optimized_villagers.sort(Comparator.comparingInt(this::getProfessionPriority)); optimized_villagers.sort(Comparator.comparingInt(villager -> {
final Villager.Profession profession = villager.getProfession();
return optimized_removal_priority.contains(profession) ? optimized_removal_priority.indexOf(profession) : Integer.MAX_VALUE;
}));
// Remove prioritized villagers that are too many // Remove prioritized villagers that are too many
for (int i = 0; i < optimized_villagers_too_many; i++) { for (int i = 0; i < optimized_villagers_too_many; i++) {
Villager villager = optimized_villagers.get(i); Villager villager = optimized_villagers.get(i);
@ -150,9 +170,4 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener, Ru
} }
} }
} }
private int getProfessionPriority(@NotNull Villager villager) {
final Villager.Profession profession = villager.getProfession();
return removal_priority.contains(profession) ? removal_priority.indexOf(profession) : Integer.MAX_VALUE;
}
} }

View File

@ -28,9 +28,10 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
private final VillagerCache villagerCache; private final VillagerCache villagerCache;
private ScheduledTask periodic_chunk_check; private ScheduledTask periodic_chunk_check;
private final List<Villager.Profession> removal_priority = new ArrayList<>(16); private final List<Villager.Profession> non_optimized_removal_priority = new ArrayList<>(16);
private final List<Villager.Profession> optimized_removal_priority = new ArrayList<>(16);
private final long check_period; private final long check_period;
private final int max_unoptimized_per_chunk, max_optimized_per_chunk; private final int non_optimized_max_per_chunk, optimized_max_per_chunk;
private final boolean log_enabled, skip_unloaded_entity_chunks; private final boolean log_enabled, skip_unloaded_entity_chunks;
protected VillagerChunkLimit() { protected VillagerChunkLimit() {
@ -41,10 +42,6 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
Checks chunks for too many villagers and removes excess villagers based on priority.\s Checks chunks for too many villagers and removes excess villagers based on priority.\s
Naturally, optimized villagers will be picked last since they don't affect performance\s Naturally, optimized villagers will be picked last since they don't affect performance\s
as much as unoptimized villagers."""); as much as unoptimized villagers.""");
this.max_unoptimized_per_chunk = config.getInt("villager-chunk-limit.max-unoptimized-per-chunk", 30,
"The maximum amount of unoptimized villagers per chunk.");
this.max_optimized_per_chunk = config.getInt("villager-chunk-limit.max-optimized-per-chunk", 20,
"The maximum amount of optimized villagers per chunk.");
this.check_period = config.getInt("villager-chunk-limit.check-period-in-ticks", 600, """ this.check_period = config.getInt("villager-chunk-limit.check-period-in-ticks", 600, """
Check all loaded chunks every X ticks. 1 second = 20 ticks\s Check all loaded chunks every X ticks. 1 second = 20 ticks\s
A shorter delay in between checks is more efficient but is also more resource intense.\s A shorter delay in between checks is more efficient but is also more resource intense.\s
@ -52,7 +49,9 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
this.skip_unloaded_entity_chunks = config.getBoolean("villager-chunk-limit.skip-if-chunk-has-not-loaded-entities", true, this.skip_unloaded_entity_chunks = config.getBoolean("villager-chunk-limit.skip-if-chunk-has-not-loaded-entities", true,
"Does not check chunks that don't have their entities loaded."); "Does not check chunks that don't have their entities loaded.");
this.log_enabled = config.getBoolean("villager-chunk-limit.log-removals", false); this.log_enabled = config.getBoolean("villager-chunk-limit.log-removals", false);
config.getList("villager-chunk-limit.removal-priority", List.of( this.non_optimized_max_per_chunk = config.getInt("villager-chunk-limit.unoptimized.max-per-chunk", 20,
"The maximum amount of unoptimized villagers per chunk.");
config.getList("villager-chunk-limit.unoptimized.removal-priority", List.of(
"NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER", "NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER",
"FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN" "FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN"
), """ ), """
@ -61,9 +60,24 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
).forEach(configuredProfession -> { ).forEach(configuredProfession -> {
try { try {
Villager.Profession profession = Villager.Profession.valueOf(configuredProfession); Villager.Profession profession = Villager.Profession.valueOf(configuredProfession);
this.removal_priority.add(profession); this.non_optimized_removal_priority.add(profession);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
LogUtil.moduleLog(Level.WARNING, "villager-chunk-limit", LogUtil.moduleLog(Level.WARNING, "villager-chunk-limit.unoptimized",
"Villager profession '"+configuredProfession+"' not recognized. " +
"Make sure you're using the correct profession enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html.");
}
});
this.optimized_max_per_chunk = config.getInt("villager-chunk-limit.optimized.max-per-chunk", 60,
"The maximum amount of optimized villagers per chunk.");
config.getList("villager-chunk-limit.optimized.removal-priority", List.of(
"NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER",
"FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN"
)).forEach(configuredProfession -> {
try {
Villager.Profession profession = Villager.Profession.valueOf(configuredProfession);
this.optimized_removal_priority.add(profession);
} catch (IllegalArgumentException e) {
LogUtil.moduleLog(Level.WARNING, "villager-chunk-limit.optimized",
"Villager profession '"+configuredProfession+"' not recognized. " + "Villager profession '"+configuredProfession+"' not recognized. " +
"Make sure you're using the correct profession enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html."); "Make sure you're using the correct profession enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html.");
} }
@ -79,7 +93,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
for (World world : server.getWorlds()) { for (World world : server.getWorlds()) {
for (Chunk chunk : world.getLoadedChunks()) { for (Chunk chunk : world.getLoadedChunks()) {
plugin.getServer().getRegionScheduler().run( plugin.getServer().getRegionScheduler().run(
plugin, world, chunk.getX(), chunk.getZ(), check_chunk -> this.checkVillagers(chunk) plugin, world, chunk.getX(), chunk.getZ(), check_chunk -> this.manageVillagerCount(chunk)
); );
} }
} }
@ -101,7 +115,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
private void onCreatureSpawn(CreatureSpawnEvent event) { private void onCreatureSpawn(CreatureSpawnEvent event) {
Entity spawned = event.getEntity(); Entity spawned = event.getEntity();
if (spawned.getType().equals(EntityType.VILLAGER)) { if (spawned.getType().equals(EntityType.VILLAGER)) {
this.checkVillagers(spawned.getChunk()); this.manageVillagerCount(spawned.getChunk());
} }
} }
@ -109,11 +123,11 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
private void onInteract(PlayerInteractEntityEvent event) { private void onInteract(PlayerInteractEntityEvent event) {
Entity clicked = event.getRightClicked(); Entity clicked = event.getRightClicked();
if (clicked.getType().equals(EntityType.VILLAGER)) { if (clicked.getType().equals(EntityType.VILLAGER)) {
this.checkVillagers(clicked.getChunk()); this.manageVillagerCount(clicked.getChunk());
} }
} }
private void checkVillagers(@NotNull Chunk chunk) { private void manageVillagerCount(@NotNull Chunk chunk) {
if (skip_unloaded_entity_chunks && !chunk.isEntitiesLoaded()) return; if (skip_unloaded_entity_chunks && !chunk.isEntitiesLoaded()) return;
// Collect all optimized and unoptimized villagers in that chunk // Collect all optimized and unoptimized villagers in that chunk
@ -132,10 +146,13 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
} }
// Check if there are more unoptimized villagers in that chunk than allowed // Check if there are more unoptimized villagers in that chunk than allowed
final int not_optimized_villagers_too_many = not_optimized_villagers.size() - max_unoptimized_per_chunk; final int not_optimized_villagers_too_many = not_optimized_villagers.size() - non_optimized_max_per_chunk;
if (not_optimized_villagers_too_many > 0) { if (not_optimized_villagers_too_many > 0) {
// Sort villagers by profession priority // Sort villagers by profession priority
not_optimized_villagers.sort(Comparator.comparingInt(this::getProfessionPriority)); not_optimized_villagers.sort(Comparator.comparingInt(villager -> {
final Villager.Profession profession = villager.getProfession();
return non_optimized_removal_priority.contains(profession) ? non_optimized_removal_priority.indexOf(profession) : Integer.MAX_VALUE;
}));
// Remove prioritized villagers that are too many // Remove prioritized villagers that are too many
for (int i = 0; i < not_optimized_villagers_too_many; i++) { for (int i = 0; i < not_optimized_villagers_too_many; i++) {
Villager villager = not_optimized_villagers.get(i); Villager villager = not_optimized_villagers.get(i);
@ -147,10 +164,13 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
} }
// Check if there are more optimized villagers in that chunk than allowed // Check if there are more optimized villagers in that chunk than allowed
final int optimized_villagers_too_many = optimized_villagers.size() - max_optimized_per_chunk; final int optimized_villagers_too_many = optimized_villagers.size() - optimized_max_per_chunk;
if (optimized_villagers_too_many > 0) { if (optimized_villagers_too_many > 0) {
// Sort villagers by profession priority // Sort villagers by profession priority
optimized_villagers.sort(Comparator.comparingInt(this::getProfessionPriority)); optimized_villagers.sort(Comparator.comparingInt(villager -> {
final Villager.Profession profession = villager.getProfession();
return optimized_removal_priority.contains(profession) ? optimized_removal_priority.indexOf(profession) : Integer.MAX_VALUE;
}));
// Remove prioritized villagers that are too many // Remove prioritized villagers that are too many
for (int i = 0; i < optimized_villagers_too_many; i++) { for (int i = 0; i < optimized_villagers_too_many; i++) {
Villager villager = optimized_villagers.get(i); Villager villager = optimized_villagers.get(i);
@ -161,9 +181,4 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
} }
} }
} }
private int getProfessionPriority(@NotNull Villager villager) {
final Villager.Profession profession = villager.getProfession();
return removal_priority.contains(profession) ? removal_priority.indexOf(profession) : Integer.MAX_VALUE;
}
} }