diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..158129713
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0e49f92aa..1e971f02e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
me.lokka30
LevelledMobs
- 3.3.3 b604
+ 3.4.0 b624
jar
LevelledMobs
@@ -143,7 +143,7 @@
io.papermc.paper
paper-api
- 1.17.1-R0.1-SNAPSHOT
+ 1.18.1-R0.1-SNAPSHOT
provided
@@ -161,13 +161,13 @@
org.spigotmc
spigot-api
- 1.17.1-R0.1-SNAPSHOT
+ 1.18.1-R0.1-SNAPSHOT
provided
org.bstats
bstats-bukkit
- 2.2.1
+ 3.0.0
compile
@@ -185,7 +185,7 @@
com.github.dmulloy2
ProtocolLib
- 4.7.0
+ 4.8.0
provided
@@ -197,13 +197,13 @@
simplepets.brainsynder
API
- 5.0-BUILD-98
+ 5.0-BUILD-146
provided
com.google.code.gson
gson
- 2.8.9
+ 2.9.0
provided
diff --git a/src/main/java/me/lokka30/levelledmobs/Companion.java b/src/main/java/me/lokka30/levelledmobs/Companion.java
index cbe76c8be..3cbb62b9d 100644
--- a/src/main/java/me/lokka30/levelledmobs/Companion.java
+++ b/src/main/java/me/lokka30/levelledmobs/Companion.java
@@ -24,9 +24,10 @@
import me.lokka30.levelledmobs.listeners.PlayerInteractEventListener;
import me.lokka30.levelledmobs.listeners.PlayerJoinListener;
import me.lokka30.levelledmobs.listeners.PlayerPortalEventListener;
-import me.lokka30.levelledmobs.managers.ExternalCompatibilityManager;
import me.lokka30.levelledmobs.managers.LevelManager;
import me.lokka30.levelledmobs.managers.PlaceholderApiIntegration;
+
+import me.lokka30.levelledmobs.misc.ChunkKillInfo;
import me.lokka30.levelledmobs.misc.DebugType;
import me.lokka30.levelledmobs.misc.FileLoader;
import me.lokka30.levelledmobs.misc.FileMigrator;
@@ -45,7 +46,10 @@
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -54,10 +58,13 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InvalidObjectException;
+import java.time.Duration;
import java.time.Instant;
-import java.util.Collections;
+
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -88,6 +95,8 @@ public class Companion {
this.spawner_CopyIds = new LinkedList<>();
this.spawner_InfoIds = new LinkedList<>();
this.debugsEnabled = new LinkedList<>();
+ this.entityDeathInChunkCounter = new HashMap<>();
+ this.chunkKillNoticationTracker = new HashMap<>();
}
final private WeakHashMap recentlyJoinedPlayers;
@@ -95,6 +104,10 @@ public class Companion {
public HashSet groups_AquaticMobs;
public HashSet groups_PassiveMobs;
public List updateResult;
+ private boolean hadRulesLoadError;
+ public boolean useAdventure;
+ final private HashMap> entityDeathInChunkCounter;
+ final private HashMap> chunkKillNoticationTracker;
final public Map playerNetherPortals;
final public Map playerWorldPortals;
final public List spawner_CopyIds;
@@ -102,8 +115,11 @@ public class Companion {
final public List debugsEnabled;
final private PluginManager pluginManager = Bukkit.getPluginManager();
final private MetricsInfo metricsInfo;
+ private BukkitTask hashMapCleanUp;
final static private Object playerLogonTimes_Lock = new Object();
final static private Object playerNetherPortals_Lock = new Object();
+ final static private Object entityDeathInChunkCounter_Lock = new Object();
+ final static private Object entityDeathInChunkNotifier_Lock = new Object();
//Checks if the server version is supported
public void checkCompatibility() {
@@ -118,8 +134,18 @@ public void checkCompatibility() {
"Compatible MC versions: &b" + String.join("&7,&b ", Utils.getSupportedServerVersions()) + "&7.");
}
- if (!ExternalCompatibilityManager.hasProtocolLibInstalled()) {
- incompatibilities.add("Your server does not have &bProtocolLib&7 installed! This means that no levelled nametags will appear on the mobs. If you wish to see custom nametags above levelled mobs, then you must install ProtocolLib.");
+ final Plugin protocolLibPlugin = Bukkit.getPluginManager().getPlugin("ProtocolLib");
+
+ if (protocolLibPlugin == null) {
+ incompatibilities.add("Your server does not have ProtocolLib installed! This means that you will" +
+ "not be able to see any custom nametags/labels above any levelled mobs' heads. To fix this, " +
+ "install the latest compatible version of ProtocolLib for your server.");
+ } else {
+ if(VersionUtils.isOneEighteen() && protocolLibPlugin.getDescription().getVersion().equals("4.7.0")) {
+ incompatibilities.add("You are running an outdated version of ProtocolLib! This version of " +
+ "ProtocolLib does not support 1.18+ servers, so you will receive lots of errors if " +
+ "you try to use it. Update to the latest ProtocolLib version as soon as possible.");
+ }
}
main.incompatibilitiesAmount = incompatibilities.size();
@@ -131,6 +157,10 @@ public void checkCompatibility() {
}
}
+ public boolean getHadRulesLoadError(){
+ return this.hadRulesLoadError;
+ }
+
private int getSettingsVersion(){
final File file = new File(main.getDataFolder(), "settings.yml");
if (!file.exists()) return 0;
@@ -146,7 +176,9 @@ boolean loadFiles(final boolean isReload) {
// save license.txt
FileLoader.saveResourceIfNotExists(main, new File(main.getDataFolder(), "license.txt"));
- main.rulesParsingManager.parseRulesMain(FileLoader.loadFile(main, "rules", FileLoader.RULES_FILE_VERSION));
+ final YamlConfiguration rulesFile = FileLoader.loadFile(main, "rules", FileLoader.RULES_FILE_VERSION);
+ this.hadRulesLoadError = rulesFile == null;
+ main.rulesParsingManager.parseRulesMain(rulesFile);
main.configUtils.playerLevellingEnabled = main.rulesManager.isPlayerLevellingEnabled();
@@ -192,6 +224,7 @@ boolean loadFiles(final boolean isReload) {
main.configUtils.load();
main.playerLevellingMinRelevelTime = main.helperSettings.getInt(main.settingsCfg, "player-levelling-relevel-min-time", 5000);
+ this.useAdventure = main.helperSettings.getBoolean(main.settingsCfg, "use-adventure", true);
return true;
}
@@ -307,6 +340,121 @@ void setupMetrics() {
metrics.addCustomChart(new SimpleBarChart("enabled-compatibility", metricsInfo::enabledCompats));
}
+ void startCleanupTask(){
+ this.hashMapCleanUp = new BukkitRunnable() {
+ @Override
+ public void run() {
+ synchronized (entityDeathInChunkCounter_Lock) {
+ chunkKillLimitCleanup();
+ }
+ synchronized (entityDeathInChunkNotifier_Lock){
+ chunkKillNoticationCleanup();
+ }
+ }
+ }.runTaskTimerAsynchronously(main, 100, 40);
+ }
+
+ private void chunkKillLimitCleanup(){
+ final List chunkKeysToRemove = new LinkedList<>();
+
+ for (final long chunkKey : entityDeathInChunkCounter.keySet()){
+ // Cooldown time, entity counts
+ final Map pairList = entityDeathInChunkCounter.get(chunkKey);
+
+ if (pairList == null) continue;
+ final Instant now = Instant.now();
+
+ for (final EntityType entityType : pairList.keySet()) {
+ final ChunkKillInfo chunkKillInfo = pairList.get(entityType);
+
+ chunkKillInfo.getEntrySet().removeIf(
+ e -> e.getKey().compareTo(now.minusSeconds(e.getValue())) < 0
+ );
+ }
+
+ pairList.entrySet().removeIf(e -> e.getValue().isEmpty());
+
+ if(pairList.isEmpty()){
+ // Remove the object to prevent iterate over exceed amount of empty pairList
+ chunkKeysToRemove.add(chunkKey);
+ }
+ }
+
+ for (final long chunkKey : chunkKeysToRemove)
+ entityDeathInChunkCounter.remove(chunkKey);
+ }
+
+ private void chunkKillNoticationCleanup(){
+ final Iterator iterator = this.chunkKillNoticationTracker.keySet().iterator();
+
+ while (iterator.hasNext()){
+ final long chunkKey = iterator.next();
+ final Map playerTimestamps = this.chunkKillNoticationTracker.get(chunkKey);
+ playerTimestamps.entrySet().removeIf(e -> Duration.between(e.getValue(), Instant.now()).toSeconds() > 30L);
+
+ if (playerTimestamps.isEmpty()) iterator.remove();
+ }
+ }
+
+ @NotNull
+ public Map getorAddPairForSpecifiedChunk(final long chunkKey){
+ synchronized (entityDeathInChunkCounter_Lock){
+ return this.entityDeathInChunkCounter.computeIfAbsent(chunkKey, k -> new HashMap<>());
+ }
+ }
+
+ @NotNull
+ public List