9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-29 11:59:24 +00:00

optimize AttributeMap (#299)

* optimize AttributeMap

* update multithreaded tracker config

* use non-sync collection when MT disabled

* cleanup
This commit is contained in:
hayanesuru
2025-04-28 23:35:26 +08:00
committed by GitHub
parent 1974f34879
commit 1bddbe19f0
2 changed files with 354 additions and 34 deletions

View File

@@ -177,7 +177,7 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6
public boolean visible = true;
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index d8298c7925e3bcea07ead4d438478cc51abcfa16..75670751064add901c2628d53d8028350f966c5d 100644
index d8298c7925e3bcea07ead4d438478cc51abcfa16..78798a84c6b49708f2650b52b40e397e4f26b532 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -110,8 +110,16 @@ public class ServerEntity {
@@ -199,24 +199,15 @@ index d8298c7925e3bcea07ead4d438478cc51abcfa16..75670751064add901c2628d53d802835
}
}
);
@@ -435,12 +443,15 @@ public class ServerEntity {
if (this.entity instanceof LivingEntity) {
Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
if (!attributesToSync.isEmpty()) {
+ // Leaf start - petal - Multithreaded tracker - send in main thread
+ final Set<AttributeInstance> copy = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(attributesToSync);
// CraftBukkit start - Send scaled max health
if (this.entity instanceof ServerPlayer serverPlayer) {
- serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false);
+ serverPlayer.getBukkitEntity().injectScaledMaxHealth(copy, false);
}
// CraftBukkit end
- this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
+ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), copy));
+ // Leaf end - petal - Multithreaded tracker - send in main thread
@@ -443,7 +451,7 @@ public class ServerEntity {
this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
}
attributesToSync.clear();
- attributesToSync.clear();
+ ((LivingEntity)this.entity).getAttributes().getAttributesIdToSync().clear(); // Leaf - Multithreaded tracker
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index d6ebc25dc5f04194edde5ad3a1166113e5542a1d..49cbdf014d0626b36eb4c451b6de09508822b7fd 100644
--- a/net/minecraft/server/level/ServerLevel.java
@@ -252,8 +243,33 @@ index 04bf8bba0d8c0d5459605253dcc3f135bf43fd95..abe79d07196de0a10a382d4c37161c7e
// Paper start - Prevent teleporting dead entities
if (this.player.isRemoved()) {
LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index d502325d693539842fd6f5485365e0e9b786b7aa..6a0b1d4f17e9c3c3152ea1696ecb4a054abd56ce 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1317,7 +1317,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.onAttributeUpdated(attributeInstance.getAttribute());
}
- attributesToUpdate.clear();
+ this.getAttributes().getAttributesIdToUpdate().clear(); // Leaf - Multithreaded tracker
}
protected void onAttributeUpdated(Holder<Attribute> attribute) {
diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java
index f8419dde44ebc7324e783f8bee42132d5ec973c3..8b18ec1644eb743483ed6ed79e9c98d287fd7e20 100644
--- a/net/minecraft/world/entity/ai/attributes/Attribute.java
+++ b/net/minecraft/world/entity/ai/attributes/Attribute.java
@@ -16,6 +16,7 @@ public class Attribute {
private boolean syncable;
private final String descriptionId;
private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE;
+ public int uniqueId; // Leaf
protected Attribute(String descriptionId, double defaultValue) {
this.defaultValue = defaultValue;
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
index 8013594bb4844e7a8abf28123958e7f632d39341..ceff383d565267edd13a6d9006030b8e1f8053e3 100644
index 8013594bb4844e7a8abf28123958e7f632d39341..80c703fe85ec21e4d218823504f4bbf7826b2fa6 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
@@ -24,8 +24,11 @@ public class AttributeInstance {
@@ -264,34 +280,329 @@ index 8013594bb4844e7a8abf28123958e7f632d39341..ceff383d565267edd13a6d9006030b8e
- private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>();
+ // Leaf start - Multithreaded tracker
+ private final boolean multiThreadedTrackingEnabled = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled;
+ private final Map<ResourceLocation, AttributeModifier> modifierById = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>();
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>();
+ private final Map<ResourceLocation, AttributeModifier> modifierById = multiThreadedTrackingEnabled ? it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this) : new Object2ObjectArrayMap<>();
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers = multiThreadedTrackingEnabled ? it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this) : new Object2ObjectArrayMap<>();
+ // Leaf end - Multithreaded tracker
private double baseValue;
private boolean dirty = true;
private double cachedValue;
@@ -114,7 +117,15 @@ public class AttributeInstance {
}
protected void setDirty() {
- this.dirty = true;
+ // Leaf start - Multithreaded tracker
+ if (multiThreadedTrackingEnabled) {
+ synchronized (this) {
+ this.dirty = true;
+ }
+ } else {
+ this.dirty = true;
+ }
+ // Leaf end - Multithreaded tracker
this.onDirty.accept(this);
}
@@ -141,6 +152,17 @@ public class AttributeInstance {
}
public double getValue() {
+ // Leaf start - Multithreaded tracker
+ if (multiThreadedTrackingEnabled) {
+ synchronized (this) {
+ if (this.dirty) {
+ this.cachedValue = this.calculateValue();
+ this.dirty = false;
+ }
+ return this.cachedValue;
+ }
+ }
+ // Leaf end - Multithreaded tracker
if (this.dirty) {
this.cachedValue = this.calculateValue();
this.dirty = false;
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..371dd51c62c9a109014851c8a1562a5cb78b18b6 100644
index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..9e26059285e56b9337478f5f21aa48741b828a76 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -19,11 +19,14 @@ import org.slf4j.Logger;
@@ -19,13 +19,13 @@ import org.slf4j.Logger;
public class AttributeMap {
private static final Logger LOGGER = LogUtils.getLogger();
+ // Leaf start - Multithreaded tracker
+ private final boolean multiThreadedTrackingEnabled = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled;
// Gale start - Lithium - replace AI attributes with optimized collections
- // Gale start - Lithium - replace AI attributes with optimized collections
- private final Map<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
- private final Set<AttributeInstance> attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
- private final Set<AttributeInstance> attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
+ private final Set<AttributeInstance> attributesToSync = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
+ private final Set<AttributeInstance> attributesToUpdate = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
// Gale end - Lithium - replace AI attributes with optimized collections
- // Gale end - Lithium - replace AI attributes with optimized collections
+ // Leaf start - Multithreaded tracker
+ private final java.util.concurrent.atomic.AtomicReferenceArray<AttributeInstance> attributes;
+ private final it.unimi.dsi.fastutil.ints.IntSet attributesToSync;
+ private final it.unimi.dsi.fastutil.ints.IntSet attributesToUpdate;
+ private final java.util.function.Consumer<AttributeInstance> onDirty;
+ // Leaf end - Multithreaded tracker
private final AttributeSupplier supplier;
private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations
- private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations
private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
public AttributeMap(AttributeSupplier supplier) {
@@ -36,54 +36,115 @@ public class AttributeMap {
this.entity = entity;
// Purpur end - Ridables
this.supplier = defaultAttributes;
- this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations
+ // Leaf start - Multithreaded tracker
+ this.attributes = new java.util.concurrent.atomic.AtomicReferenceArray<>(BuiltInRegistries.ATTRIBUTE.size());
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ this.attributesToSync = it.unimi.dsi.fastutil.ints.IntSets.synchronize(new it.unimi.dsi.fastutil.ints.IntArraySet());
+ this.attributesToUpdate = it.unimi.dsi.fastutil.ints.IntSets.synchronize(new it.unimi.dsi.fastutil.ints.IntArraySet());
+ } else {
+ this.attributesToSync = new it.unimi.dsi.fastutil.ints.IntArraySet();
+ this.attributesToUpdate = new it.unimi.dsi.fastutil.ints.IntArraySet();
+ }
+ this.onDirty = this::onAttributeModified;
+ // Leaf end - Multithreaded tracker
}
private void onAttributeModified(AttributeInstance instance) {
- this.attributesToUpdate.add(instance);
+ this.attributesToUpdate.add(instance.getAttribute().value().uniqueId);
if (instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))) { // Purpur - Ridables
- this.attributesToSync.add(instance);
+ this.attributesToSync.add(instance.getAttribute().value().uniqueId);
}
}
- public Set<AttributeInstance> getAttributesToSync() {
+ // Leaf start - Multithreaded tracker
+ public it.unimi.dsi.fastutil.ints.IntSet getAttributesIdToSync() {
return this.attributesToSync;
}
- public Set<AttributeInstance> getAttributesToUpdate() {
+ public it.unimi.dsi.fastutil.ints.IntSet getAttributesIdToUpdate() {
return this.attributesToUpdate;
}
+ @Deprecated
+ public Set<AttributeInstance> getAttributesToSync() {
+ var set = new it.unimi.dsi.fastutil.objects.ObjectArraySet<AttributeInstance>();
+ for (int i : this.attributesToSync.toIntArray()) {
+ var attribute = this.attributes.get(i);
+ if (attribute == null) {
+ throw new NullPointerException("attribute cannot be null");
+ }
+ set.add(attribute);
+ }
+ return set;
+ }
+
+ @Deprecated
+ public Set<AttributeInstance> getAttributesToUpdate() {
+ var set = new it.unimi.dsi.fastutil.objects.ObjectArraySet<AttributeInstance>();
+ for (int i : this.attributesToUpdate.toIntArray()) {
+ var attribute = this.attributes.get(i);
+ if (attribute == null) {
+ throw new NullPointerException("attribute cannot be null");
+ }
+ set.add(attribute);
+ }
+ return set;
+ }
+ // Leaf end - Multithreaded tracker
+
public Collection<AttributeInstance> getSyncableAttributes() {
- return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))).collect(Collectors.toList()); // Purpur - Ridables
+ // Leaf start
+ var attrs = this.attributes;
+ var result = new java.util.ArrayList<AttributeInstance>();
+ int len = attrs.length();
+ for (int i = 0; i < len; i++) {
+ AttributeInstance instance = attrs.get(i);
+ if (instance != null
+ && instance.getAttribute().value().isClientSyncable()
+ && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))
+ ) {
+ result.add(instance);
+ }
+ }
+ return result;
+ // Leaf end
}
@Nullable
public AttributeInstance getInstance(Holder<Attribute> attribute) {
- return this.attributes.computeIfAbsent(attribute, this.createInstance); // Gale - Airplane - reduce entity allocations - cache lambda, as for some reason java allocates it anyways
+ // Leaf start
+ int id = attribute.value().uniqueId;
+ var attr = this.attributes.getAcquire(id);
+ if (attr != null) {
+ return attr;
+ }
+ var newAttr = this.supplier.createInstance(this.onDirty, attribute);
+ attributes.compareAndExchangeRelease(id, null, newAttr);
+ return attributes.getAcquire(id);
+ // Leaf end
}
public boolean hasAttribute(Holder<Attribute> attribute) {
- return this.attributes.get(attribute) != null || this.supplier.hasAttribute(attribute);
+ return this.attributes.get(attribute.value().uniqueId) != null || this.supplier.hasAttribute(attribute); // Leaf
}
public boolean hasModifier(Holder<Attribute> attribute, ResourceLocation id) {
- AttributeInstance attributeInstance = this.attributes.get(attribute);
+ AttributeInstance attributeInstance = this.attributes.get(attribute.value().uniqueId); // Leaf
return attributeInstance != null ? attributeInstance.getModifier(id) != null : this.supplier.hasModifier(attribute, id);
}
public double getValue(Holder<Attribute> attribute) {
- AttributeInstance attributeInstance = this.attributes.get(attribute);
+ AttributeInstance attributeInstance = this.attributes.get(attribute.value().uniqueId); // Leaf
return attributeInstance != null ? attributeInstance.getValue() : this.supplier.getValue(attribute);
}
public double getBaseValue(Holder<Attribute> attribute) {
- AttributeInstance attributeInstance = this.attributes.get(attribute);
+ AttributeInstance attributeInstance = this.attributes.get(attribute.value().uniqueId); // Leaf
return attributeInstance != null ? attributeInstance.getBaseValue() : this.supplier.getBaseValue(attribute);
}
public double getModifierValue(Holder<Attribute> attribute, ResourceLocation id) {
- AttributeInstance attributeInstance = this.attributes.get(attribute);
+ AttributeInstance attributeInstance = this.attributes.get(attribute.value().uniqueId); // Leaf
return attributeInstance != null ? attributeInstance.getModifier(id).amount() : this.supplier.getModifierValue(attribute, id);
}
@@ -99,7 +160,7 @@ public class AttributeMap {
public void removeAttributeModifiers(Multimap<Holder<Attribute>, AttributeModifier> modifiers) {
modifiers.asMap().forEach((holder, collection) -> {
- AttributeInstance attributeInstance = this.attributes.get(holder);
+ AttributeInstance attributeInstance = this.attributes.get(holder.value().uniqueId); // Leaf
if (attributeInstance != null) {
collection.forEach(attributeModifier -> attributeInstance.removeModifier(attributeModifier.id()));
}
@@ -107,37 +168,58 @@ public class AttributeMap {
}
public void assignAllValues(AttributeMap map) {
- map.attributes.values().forEach(attribute -> {
- AttributeInstance instance = this.getInstance(attribute.getAttribute());
+ // Leaf start
+ var attrs = map.attributes;
+ int len = attrs.length();
+ for (int i = 0; i < len; i++) {
+ AttributeInstance instance = attrs.get(i);
if (instance != null) {
- instance.replaceFrom(attribute);
+ AttributeInstance self = this.getInstance(instance.getAttribute());
+ if (self != null) {
+ self.replaceFrom(instance);
+ }
}
- });
+ }
+ // Leaf end
}
public void assignBaseValues(AttributeMap map) {
- map.attributes.values().forEach(attribute -> {
- AttributeInstance instance = this.getInstance(attribute.getAttribute());
+ // Leaf start
+ var attrs = map.attributes;
+ int len = attrs.length();
+ for (int i = 0; i < len; i++) {
+ AttributeInstance instance = attrs.get(i);
if (instance != null) {
- instance.setBaseValue(attribute.getBaseValue());
+ AttributeInstance self = this.getInstance(instance.getAttribute());
+ if (self != null) {
+ self.setBaseValue(instance.getBaseValue());
+ }
}
- });
+ }
+ // Leaf end
}
public void assignPermanentModifiers(AttributeMap map) {
- map.attributes.values().forEach(attribute -> {
- AttributeInstance instance = this.getInstance(attribute.getAttribute());
+ // Leaf start
+ var attrs = map.attributes;
+ int len = attrs.length();
+ for (int i = 0; i < len; i++) {
+ AttributeInstance instance = attrs.get(i);
if (instance != null) {
- instance.addPermanentModifiers(attribute.getPermanentModifiers());
+ AttributeInstance self = this.getInstance(instance.getAttribute());
+ if (self != null) {
+ self.addPermanentModifiers(instance.getPermanentModifiers());
+ }
}
- });
+ }
+ // Leaf end
}
public boolean resetBaseValue(Holder<Attribute> attribute) {
if (!this.supplier.hasAttribute(attribute)) {
return false;
} else {
- AttributeInstance attributeInstance = this.attributes.get(attribute);
+ AttributeInstance attributeInstance = this.attributes.get(attribute.value().uniqueId); // Leaf
if (attributeInstance != null) {
attributeInstance.setBaseValue(this.supplier.getBaseValue(attribute));
}
@@ -149,9 +231,16 @@ public class AttributeMap {
public ListTag save() {
ListTag listTag = new ListTag();
- for (AttributeInstance attributeInstance : this.attributes.values()) {
- listTag.add(attributeInstance.save());
+ // Leaf start
+ var attrs = this.attributes;
+ int len = attrs.length();
+ for (int i = 0; i < len; i++) {
+ AttributeInstance instance = attrs.get(i);
+ if (instance != null) {
+ listTag.add(instance.save());
+ }
}
+ // Leaf end
return listTag;
}
@@ -177,7 +266,7 @@ public class AttributeMap {
// Paper - start - living entity allow attribute registration
public void registerAttribute(Holder<Attribute> attributeBase) {
AttributeInstance attributeModifiable = new AttributeInstance(attributeBase, AttributeInstance::getAttribute);
- attributes.put(attributeBase, attributeModifiable);
+ attributes.setRelease(attributeBase.value().uniqueId, attributeModifiable); // Leaf
}
// Paper - end - living entity allow attribute registration
diff --git a/net/minecraft/world/entity/ai/attributes/Attributes.java b/net/minecraft/world/entity/ai/attributes/Attributes.java
index 26e4f766640f28068b6c07174285768e7d251b4f..59dca559ff875d710ecc5bafda23b7125b86b8ac 100644
--- a/net/minecraft/world/entity/ai/attributes/Attributes.java
+++ b/net/minecraft/world/entity/ai/attributes/Attributes.java
@@ -6,6 +6,8 @@ import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
public class Attributes {
+ private static final java.util.concurrent.atomic.AtomicInteger LENGTH = new java.util.concurrent.atomic.AtomicInteger(0); // Leaf
+
public static final Holder<Attribute> ARMOR = register("armor", new RangedAttribute("attribute.name.armor", 0.0, 0.0, 30.0).setSyncable(true));
public static final Holder<Attribute> ARMOR_TOUGHNESS = register(
"armor_toughness", new RangedAttribute("attribute.name.armor_toughness", 0.0, 0.0, 20.0).setSyncable(true)
@@ -93,6 +95,7 @@ public class Attributes {
);
private static Holder<Attribute> register(String name, Attribute attribute) {
+ attribute.uniqueId = LENGTH.getAndIncrement(); // Leaf
return Registry.registerForHolder(BuiltInRegistries.ATTRIBUTE, ResourceLocation.withDefaultNamespace(name), attribute);
}
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
index 681dec447486138088fe5f705ef4fadab531139f..27f8a22d798a17dbd5949d1b6ff0526837fe91d5 100644
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java

View File

@@ -15,6 +15,7 @@ public class MultithreadedTracker extends ConfigModules {
public static int asyncEntityTrackerMaxThreads = 0;
public static int asyncEntityTrackerKeepalive = 60;
public static int asyncEntityTrackerQueueSize = 0;
private static boolean asyncMultithreadedTrackerInitialized;
@Override
public void onLoaded() {
@@ -25,15 +26,23 @@ public class MultithreadedTracker extends ConfigModules {
"""
异步实体跟踪,
在实体数量多且密集的情况下效果明显.""");
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
compatModeEnabled = config.getBoolean(getBasePath() + ".compat-mode", compatModeEnabled, config.pickStringRegionBased("""
config.addCommentRegionBased(getBasePath() + ".compat-mode",
"""
Enable compat mode ONLY if Citizens or NPC plugins using real entity has installed,
Compat mode fixed visible issue with player type NPCs of Citizens,
But still recommend to use packet based / virtual entity NPC plugin, e.g. ZNPC Plus, Adyeshach, Fancy NPC or else.""",
"""
是否启用兼容模式,
如果你的服务器安装了 Citizens 或其他类似非发包 NPC 插件, 请开启此项."""));
如果你的服务器安装了 Citizens 或其他类似非发包 NPC 插件, 请开启此项.""");
if (asyncMultithreadedTrackerInitialized) {
config.getConfigSection(getBasePath());
return;
}
asyncMultithreadedTrackerInitialized = true;
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
compatModeEnabled = config.getBoolean(getBasePath() + ".compat-mode", compatModeEnabled);
asyncEntityTrackerMaxThreads = config.getInt(getBasePath() + ".max-threads", asyncEntityTrackerMaxThreads);
asyncEntityTrackerKeepalive = config.getInt(getBasePath() + ".keepalive", asyncEntityTrackerKeepalive);
asyncEntityTrackerQueueSize = config.getInt(getBasePath() + ".queue-size", asyncEntityTrackerQueueSize);