From bdb5f4586c37950f728a36a6c1b05ffb2d41a0ba Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 19 Jul 2025 22:48:59 +0900 Subject: [PATCH] optimize brain collections --- ...lace-brain-with-optimized-collection.patch | 151 ++++++------ .../dreeam/leaf/util/RegistryTypeManager.java | 15 +- .../leaf/util/map/ActivityArrayMap.java | 215 ++++++++++-------- .../dreeam/leaf/util/map/ActivityBitSet.java | 43 ++-- .../util/map/AttributeInstanceArrayMap.java | 2 +- .../util/map/BehaviorControlArraySet.java | 140 +++++++++++- 6 files changed, 374 insertions(+), 192 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0156-Replace-brain-with-optimized-collection.patch b/leaf-server/minecraft-patches/features/0156-Replace-brain-with-optimized-collection.patch index 62d5d611..6b99656f 100644 --- a/leaf-server/minecraft-patches/features/0156-Replace-brain-with-optimized-collection.patch +++ b/leaf-server/minecraft-patches/features/0156-Replace-brain-with-optimized-collection.patch @@ -4,12 +4,13 @@ Date: Sat, 26 Oct 2024 00:06:04 +0800 Subject: [PATCH] Replace brain with optimized collection Co-authored-by: Taiyou06 +Co-authored-by: hayanesuru diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java -index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..1d533dc2db5df6f31f307bc94f9f8e5784b0ab23 100644 +index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..c1fa839b69626e0694c091fd91ea1502088845f5 100644 --- a/net/minecraft/world/entity/ai/Brain.java +++ b/net/minecraft/world/entity/ai/Brain.java -@@ -45,14 +45,20 @@ public class Brain { +@@ -45,14 +45,21 @@ public class Brain { static final Logger LOGGER = LogUtils.getLogger(); private final Supplier>> codec; private static final int SCHEDULE_UPDATE_DELAY = 20; @@ -20,24 +21,25 @@ index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..1d533dc2db5df6f31f307bc94f9f8e57 + private final Map, Optional>> memories = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(); + private final Map>, Sensor> sensors = new it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap<>(); + private final Map>>> availableBehaviorsByPriority = new it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap<>(); -+ public static final Map[] EMPTY_ARRAY = {}; -+ private Map>>[] availableBehaviorsByPriorityArray = EMPTY_ARRAY; -+ // Leaf end - Replace brain maps with optimized collection ++ public static final Map[] EMPTY_MAP_ARRAY = {}; ++ public static final Set[] EMPTY_SET_ARRAY = {}; ++ public static final org.dreeam.leaf.util.map.BehaviorControlArraySet[] EMPTY_BEHAVIOR_ARRAY = {}; ++ private Map>>[] availableBehaviorsByPriorityArray = EMPTY_MAP_ARRAY; ++ private org.dreeam.leaf.util.map.BehaviorControlArraySet[] activeBehaviors = EMPTY_BEHAVIOR_ARRAY; private Schedule schedule = Schedule.EMPTY; - private final Map, MemoryStatus>>> activityRequirements = Maps.newHashMap(); - private final Map>> activityMemoriesToEraseWhenStopped = Maps.newHashMap(); - private Set coreActivities = Sets.newHashSet(); - private final Set activeActivities = Sets.newHashSet(); -+ // Leaf start - Replace brain maps with optimized collection -+ private final Map, MemoryStatus>>> activityRequirements = new org.dreeam.leaf.util.map.ActivityArrayMap<>(); -+ private final Map>> activityMemoriesToEraseWhenStopped = new org.dreeam.leaf.util.map.ActivityArrayMap<>(); -+ private Set coreActivities = new org.dreeam.leaf.util.map.ActivityBitSet(); ++ private final Map, MemoryStatus>>> activityRequirements = new org.dreeam.leaf.util.map.ActivityArrayMap<>(EMPTY_SET_ARRAY); ++ private final Map>> activityMemoriesToEraseWhenStopped = new org.dreeam.leaf.util.map.ActivityArrayMap<>(EMPTY_SET_ARRAY); ++ private Set coreActivities = Set.of(); + private final Set activeActivities = new org.dreeam.leaf.util.map.ActivityBitSet(); + // Leaf end - Replace brain maps with optimized collection private Activity defaultActivity = Activity.IDLE; private long lastScheduleUpdate = -9999L; -@@ -162,6 +168,8 @@ public class Brain { +@@ -162,6 +169,8 @@ public class Brain { for (Brain.MemoryValue memoryValue : memoryValues) { memoryValue.setMemoryInternal(this); } @@ -46,15 +48,16 @@ index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..1d533dc2db5df6f31f307bc94f9f8e57 } public DataResult serializeStart(DynamicOps ops) { -@@ -178,6 +186,7 @@ public class Brain { +@@ -178,6 +187,8 @@ public class Brain { public void clearMemories() { this.memories.keySet().forEach(memoryModuleType -> this.memories.put((MemoryModuleType)memoryModuleType, Optional.empty())); -+ this.availableBehaviorsByPriorityArray = EMPTY_ARRAY; // Leaf ++ this.availableBehaviorsByPriorityArray = EMPTY_MAP_ARRAY; // Leaf - Replace brain maps with optimized collection ++ this.activeBehaviors = EMPTY_BEHAVIOR_ARRAY; // Leaf - Replace brain maps with optimized collection } public void eraseMemory(MemoryModuleType type) { -@@ -362,7 +371,7 @@ public class Brain { +@@ -362,7 +373,7 @@ public class Brain { } public void addActivity(Activity activity, ImmutableList>> tasks) { @@ -63,26 +66,28 @@ index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..1d533dc2db5df6f31f307bc94f9f8e57 } public void addActivityWithConditions( -@@ -395,8 +404,8 @@ public class Brain { +@@ -395,15 +406,18 @@ public class Brain { for (Pair> pair : tasks) { this.availableBehaviorsByPriority - .computeIfAbsent(pair.getFirst(), integer -> Maps.newHashMap()) - .computeIfAbsent(activity, activity1 -> Sets.newLinkedHashSet()) -+ .computeIfAbsent(pair.getFirst(), integer -> new org.dreeam.leaf.util.map.ActivityArrayMap<>()) // Leaf - Replace brain activity maps with optimized collection -+ .computeIfAbsent(activity, activity1 -> new org.dreeam.leaf.util.map.BehaviorControlArraySet<>()) // Leaf - Replace brain activity maps with optimized collection ++ .computeIfAbsent(pair.getFirst(), integer -> new org.dreeam.leaf.util.map.ActivityArrayMap<>((Set>[]) EMPTY_SET_ARRAY)) // Leaf - Replace brain maps with optimized collection ++ .computeIfAbsent(activity, activity1 -> new org.dreeam.leaf.util.map.BehaviorControlArraySet<>()) // Leaf - Replace brain maps with optimized collection .add((BehaviorControl)pair.getSecond()); } ++ if (this.activeBehaviors != EMPTY_BEHAVIOR_ARRAY) this.activeBehaviors = EMPTY_BEHAVIOR_ARRAY; // Leaf - Replace brain maps with optimized collection } -@@ -404,6 +413,7 @@ public class Brain { + @VisibleForTesting public void removeAllBehaviors() { this.availableBehaviorsByPriority.clear(); -+ this.availableBehaviorsByPriorityArray = EMPTY_ARRAY; // Leaf ++ this.availableBehaviorsByPriorityArray = EMPTY_MAP_ARRAY; // Leaf - Replace brain maps with optimized collection ++ this.activeBehaviors = EMPTY_BEHAVIOR_ARRAY; // Leaf - Replace brain maps with optimized collection } public boolean isActive(Activity activity) { -@@ -437,14 +447,28 @@ public class Brain { +@@ -437,14 +451,28 @@ public class Brain { } private void forgetOutdatedMemories() { @@ -114,34 +119,31 @@ index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..1d533dc2db5df6f31f307bc94f9f8e57 } } } -@@ -452,34 +476,92 @@ public class Brain { +@@ -452,34 +480,94 @@ public class Brain { public void stopAll(ServerLevel level, E owner) { long gameTime = owner.level().getGameTime(); - for (BehaviorControl behaviorControl : this.getRunningBehaviors()) { - behaviorControl.doStop(level, owner, gameTime); -+ // Leaf start ++ // Leaf start - Replace brain maps with optimized collection + if (this.availableBehaviorsByPriorityArray.length != this.availableBehaviorsByPriority.size()) { -+ this.availableBehaviorsByPriorityArray = this.availableBehaviorsByPriority.values().toArray(EMPTY_ARRAY); - } ++ this.availableBehaviorsByPriorityArray = this.availableBehaviorsByPriority.values().toArray(EMPTY_MAP_ARRAY); ++ } + for (Map>> map : availableBehaviorsByPriorityArray) { + var map1 = (org.dreeam.leaf.util.map.ActivityArrayMap>>) map; -+ for (int index = 0; index <= org.dreeam.leaf.util.map.ActivityArrayMap.BITS; index++) { -+ if ((map1.bitset & (1 << index)) != 0) { -+ var behaviorControls = (org.dreeam.leaf.util.map.BehaviorControlArraySet) map1.a[index]; -+ var behaviorControlsRaw = behaviorControls.raw(); -+ var behaviorControlsSize = behaviorControls.size(); -+ for (int i = 0; i < behaviorControlsSize; i++) { -+ BehaviorControl behaviorControl = (BehaviorControl) behaviorControlsRaw[i]; -+ if (behaviorControl.getStatus() == Behavior.Status.RUNNING) { -+ behaviorControl.doStop(level, owner, gameTime); -+ behaviorControls.dec(); -+ } ++ for (int index = 0, size = map1.size(); index < size; index++) { ++ var behaviorControls = (org.dreeam.leaf.util.map.BehaviorControlArraySet) map1.v[index]; ++ var behaviorControlsRaw = behaviorControls.raw(); ++ for (int i = 0, size1 = behaviorControls.size(); i < size1; i++) { ++ BehaviorControl behaviorControl = behaviorControlsRaw[i]; ++ if (behaviorControl.getStatus() == Behavior.Status.RUNNING) { ++ behaviorControl.doStop(level, owner, gameTime); ++ behaviorControls.dec(); + } + } + } -+ } -+ // Leaf end + } ++ // Leaf end - Replace brain maps with optimized collection } private void startEachNonRunningBehavior(ServerLevel level, E entity) { @@ -152,35 +154,44 @@ index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..1d533dc2db5df6f31f307bc94f9f8e57 - Activity activity = entry.getKey(); - if (this.activeActivities.contains(activity)) { - for (BehaviorControl behaviorControl : entry.getValue()) { -+ // Leaf start +- if (behaviorControl.getStatus() == Behavior.Status.STOPPED) { +- behaviorControl.tryStart(level, entity, gameTime); +- } ++ // Leaf start - Replace brain maps with optimized collection + if (this.availableBehaviorsByPriorityArray.length != this.availableBehaviorsByPriority.size()) { -+ this.availableBehaviorsByPriorityArray = this.availableBehaviorsByPriority.values().toArray(EMPTY_ARRAY); ++ this.availableBehaviorsByPriorityArray = this.availableBehaviorsByPriority.values().toArray(EMPTY_MAP_ARRAY); + } + var aact = (org.dreeam.leaf.util.map.ActivityBitSet) this.activeActivities; -+ for (int index = 0; index <= org.dreeam.leaf.util.map.ActivityArrayMap.BITS; index++) { -+ if ((aact.bitset & (1 << index)) != 0) { -+ for (Map>> map : availableBehaviorsByPriorityArray) { -+ var map1 = ((org.dreeam.leaf.util.map.ActivityArrayMap>>) map); -+ var ele = map1.a[index]; ++ if (activeBehaviors == EMPTY_BEHAVIOR_ARRAY || aact.unsetDirty()) { ++ var list = it.unimi.dsi.fastutil.objects.ReferenceArrayList.wrap(activeBehaviors); ++ list.clear(); ++ for (Map>> map : availableBehaviorsByPriorityArray) { ++ for (int index = 0; index < org.dreeam.leaf.util.RegistryTypeManager.ACTIVITY_SIZE; index++) { ++ if ((aact.bitset & (1 << index)) == 0) { ++ continue; ++ } ++ var ele = ((org.dreeam.leaf.util.map.ActivityArrayMap>>) map).getValue(index); + if (ele == null) { + continue; + } -+ var behaviorControls = (org.dreeam.leaf.util.map.BehaviorControlArraySet) ele; -+ var behaviorControlsRaw = behaviorControls.raw(); -+ var behaviorControlsSize = behaviorControls.size(); -+ for (int i = 0; i < behaviorControlsSize; i++) { -+ BehaviorControl behaviorControl = (BehaviorControl) behaviorControlsRaw[i]; - if (behaviorControl.getStatus() == Behavior.Status.STOPPED) { -- behaviorControl.tryStart(level, entity, gameTime); -+ if (behaviorControl.tryStart(level, entity, gameTime)) { -+ behaviorControls.inc(); -+ } - } ++ list.add((org.dreeam.leaf.util.map.BehaviorControlArraySet) ele); ++ } ++ } ++ activeBehaviors = it.unimi.dsi.fastutil.objects.ObjectArrays.setLength(list.elements(), list.size()); ++ } ++ for (int index = 0, size = activeBehaviors.length; index < size; index++) { ++ var behaviorControls = activeBehaviors[index]; ++ var behaviorControlsRaw = behaviorControls.raw(); ++ for (int i = 0, size1 = behaviorControls.size(); i < size1; i++) { ++ BehaviorControl behaviorControl = behaviorControlsRaw[i]; ++ if (behaviorControl.getStatus() == Behavior.Status.STOPPED) { ++ if (behaviorControl.tryStart(level, entity, gameTime)) { ++ behaviorControls.inc(); } } } } -+ // Leaf end ++ // Leaf end - Replace brain maps with optimized collection } private void tickEachRunningBehavior(ServerLevel level, E entity) { @@ -188,32 +199,30 @@ index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..1d533dc2db5df6f31f307bc94f9f8e57 - for (BehaviorControl behaviorControl : this.getRunningBehaviors()) { - behaviorControl.tickOrStop(level, entity, gameTime); -+ // Leaf start ++ // Leaf start - Replace brain maps with optimized collection + if (this.availableBehaviorsByPriorityArray.length != this.availableBehaviorsByPriority.size()) { + this.availableBehaviorsByPriorityArray = this.availableBehaviorsByPriority.values().toArray(new Map[0]); + } + for (Map>> map : availableBehaviorsByPriorityArray) { + var map1 = (org.dreeam.leaf.util.map.ActivityArrayMap>>) map; -+ for (int index = 0; index <= org.dreeam.leaf.util.map.ActivityArrayMap.BITS; index++) { -+ if ((map1.bitset & (1 << index)) != 0) { -+ var behaviorControls = (org.dreeam.leaf.util.map.BehaviorControlArraySet) map1.a[index]; -+ if (behaviorControls.running()) { -+ var behaviorControlsRaw = behaviorControls.raw(); -+ var behaviorControlsSize = behaviorControls.size(); -+ for (int i = 0; i < behaviorControlsSize; i++) { -+ BehaviorControl behaviorControl = (BehaviorControl) behaviorControlsRaw[i]; -+ if (behaviorControl.getStatus() == Behavior.Status.RUNNING) { -+ behaviorControl.tickOrStop(level, entity, gameTime); -+ if (behaviorControl.getStatus() == Behavior.Status.STOPPED) { -+ behaviorControls.dec(); -+ } -+ } ++ for (int index = 0, size = map1.size(); index < size; index++) { ++ var behaviorControls = (org.dreeam.leaf.util.map.BehaviorControlArraySet) map1.v[index]; ++ if (!behaviorControls.running()) { ++ continue; ++ } ++ BehaviorControl[] behaviorControlsRaw = behaviorControls.raw(); ++ for (int i = 0, size1 = behaviorControls.size(); i < size1; i++) { ++ BehaviorControl behaviorControl = behaviorControlsRaw[i]; ++ if (behaviorControl.getStatus() == Behavior.Status.RUNNING) { ++ behaviorControl.tickOrStop(level, entity, gameTime); ++ if (behaviorControl.getStatus() == Behavior.Status.STOPPED) { ++ behaviorControls.dec(); + } + } + } + } } -+ // Leaf end ++ // Leaf end - Replace brain maps with optimized collection } private boolean activityRequirementsAreMet(Activity activity) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/RegistryTypeManager.java b/leaf-server/src/main/java/org/dreeam/leaf/util/RegistryTypeManager.java index be91b4ff..f1904487 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/RegistryTypeManager.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/RegistryTypeManager.java @@ -7,11 +7,20 @@ public final class RegistryTypeManager { /** * The total number of attributes in the Built-in Registry. */ - public static final int ATTRIBUTE_ID_COUNTER; + public static final int ATTRIBUTE_SIZE; + public static final int ACTIVITY_SIZE; static { - ATTRIBUTE_ID_COUNTER = BuiltInRegistries.ATTRIBUTE.size(); + ATTRIBUTE_SIZE = BuiltInRegistries.ATTRIBUTE.size(); + ACTIVITY_SIZE = BuiltInRegistries.ACTIVITY.size(); + if (ATTRIBUTE_SIZE == 0 || ACTIVITY_SIZE == 0) { + throw new ExceptionInInitializerError("RegistryTypeManager initialize before registries bootstrap"); + } + if (ACTIVITY_SIZE > 32) { + throw new ExceptionInInitializerError("minecraft:activity out of range int bitset (>32)"); + } } - private RegistryTypeManager() {} + private RegistryTypeManager() { + } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/ActivityArrayMap.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/ActivityArrayMap.java index 055298a9..12b121b6 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/ActivityArrayMap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/ActivityArrayMap.java @@ -7,75 +7,117 @@ import java.util.*; import java.util.AbstractMap.SimpleEntry; public final class ActivityArrayMap implements Map { - public static final int BITS = 25; - public int bitset = 0; - public final Object[] a = new Object[BITS + 1]; + public int[] k; + public V[] v; private int size = 0; - + private int bitset = 0; private transient KeySet keySet; - private transient Values valuesCollection; + private transient Values values; private transient EntrySet entrySet; - public ActivityArrayMap() { - if (BuiltInRegistries.ACTIVITY.size() != BITS + 1) { - throw new IllegalStateException("Unexpected registry minecraft:activity size"); + public ActivityArrayMap(V[] arr) { + this.k = new int[arr.length]; + this.v = arr; + } + + private int findIndex(int activity) { + int mask = 1 << activity; + if ((bitset & mask) == 0) { + return -1; } + for (int i = 0; i < size; i++) { + if (k[i] == activity) { + return i; + } + } + return -1; + } + + private void ensureCap() { + if (size >= k.length) { + int newCapacity = Math.max(2, k.length + k.length / 2); + k = Arrays.copyOf(k, newCapacity); + v = Arrays.copyOf(v, newCapacity); + } + } + + private void removeAtIndex(int index) { + if (index < 0 || index >= size) { + return; + } + bitset &= ~(1 << k[index]); + System.arraycopy(k, index + 1, k, index, size - index - 1); + System.arraycopy(v, index + 1, v, index, size - index - 1); + + size--; + v[size] = null; } @Override public V put(Activity key, V value) { - int index = key.id; - int mask = 1 << index; - @SuppressWarnings("unchecked") - V oldValue = (V) a[index]; - boolean hadValue = oldValue != null; - a[index] = value; - if (!hadValue && value != null) { + int index = findIndex(key.id); + if (index >= 0) { + final V oldValue = v[index]; + if (value == null) { + removeAtIndex(index); + } else { + v[index] = value; + } + return oldValue; + } else if (value != null) { + ensureCap(); + k[size] = key.id; + v[size] = value; + bitset |= (1 << key.id); size++; - bitset |= mask; } - if (hadValue && value == null) { - size--; - bitset &= ~mask; - } - return oldValue; + return null; } @Override public V get(Object key) { if (key instanceof Activity activity) { - @SuppressWarnings("unchecked") - V value = (V) a[activity.id]; - return value; + int index = findIndex(activity.id); + if (index >= 0) { + return v[index]; + } + } + return null; + } + + public V getValue(int key) { + int index = findIndex(key); + if (index >= 0) { + return v[index]; } return null; } @Override public boolean containsKey(Object key) { - return key instanceof Activity activity && a[activity.id] != null; + if (!(key instanceof Activity activity)) { + return false; + } + return (bitset & 1 << activity.id) != 0; } @Override public V remove(Object key) { if (key instanceof Activity activity) { - int index = activity.id; - @SuppressWarnings("unchecked") - V oldValue = (V) a[index]; - if (oldValue != null) { - a[index] = null; - bitset &= ~(1 << index); - size--; + int index = findIndex(activity.id); + if (index >= 0) { + V oldValue = v[index]; + removeAtIndex(index); + return oldValue; } - return oldValue; } return null; } @Override public void clear() { - Arrays.fill(a, null); + Arrays.fill(v, 0, size, null); size = 0; bitset = 0; } @@ -92,8 +134,10 @@ public final class ActivityArrayMap implements Map { @Override public boolean containsValue(Object value) { - for (Object v : a) { - if (Objects.equals(v, value)) return true; + for (int i = 0; i < size; i++) { + if (Objects.equals(v[i], value)) { + return true; + } } return false; } @@ -115,10 +159,10 @@ public final class ActivityArrayMap implements Map { @Override public Collection values() { - if (valuesCollection == null) { - valuesCollection = new Values(); + if (values == null) { + values = new Values(); } - return valuesCollection; + return values; } @Override @@ -133,28 +177,27 @@ public final class ActivityArrayMap implements Map { @Override public Iterator iterator() { return new Iterator<>() { - private int index = -1; - - private void advance() { - do index++; - while (index <= BITS && (bitset & (1 << index)) == 0); - } - - { - advance(); - } + private int index = 0; + private int lastReturned = -1; @Override public boolean hasNext() { - return index <= BITS; + return index < size; } @Override public Activity next() { if (!hasNext()) throw new NoSuchElementException(); - Activity activity = BuiltInRegistries.ACTIVITY.byIdOrThrow(index); - advance(); - return activity; + lastReturned = index; + return BuiltInRegistries.ACTIVITY.byIdOrThrow(k[index++]); + } + + @Override + public void remove() { + if (lastReturned < 0) throw new IllegalStateException(); + ActivityArrayMap.this.removeAtIndex(lastReturned); + index = lastReturned; + lastReturned = -1; } }; } @@ -179,29 +222,17 @@ public final class ActivityArrayMap implements Map { @Override public Iterator iterator() { return new Iterator<>() { - private int index = -1; - - private void advance() { - do index++; - while (index <= BITS && (bitset & (1 << index)) == 0); - } - - { - advance(); - } + private int index = 0; @Override public boolean hasNext() { - return index <= BITS; + return index < size; } @Override public V next() { if (!hasNext()) throw new NoSuchElementException(); - @SuppressWarnings("unchecked") - V value = (V) a[index]; - advance(); - return value; + return v[index++]; } }; } @@ -226,43 +257,30 @@ public final class ActivityArrayMap implements Map { @Override public Iterator> iterator() { return new Iterator<>() { - private int index = -1; - private int last = -1; - - private void advance() { - do index++; - while (index <= BITS && (bitset & (1 << index)) == 0); - } - - { - advance(); - } + private int index = 0; + private int lastReturned = -1; @Override public boolean hasNext() { - return index <= BITS; + return index < size; } @Override public Entry next() { if (!hasNext()) throw new NoSuchElementException(); - last = index; - Activity activity = BuiltInRegistries.ACTIVITY.byIdOrThrow(index); - @SuppressWarnings("unchecked") - V value = (V) a[index]; - advance(); - return new SimpleEntry<>(activity, value); + lastReturned = index; + int key = k[index]; + V value = v[index]; + index++; + return new SimpleEntry<>(BuiltInRegistries.ACTIVITY.byIdOrThrow(key), value); } @Override public void remove() { - if (last == -1) throw new IllegalStateException(); - if (a[last] != null) { - a[last] = null; - bitset &= ~(1 << last); - size--; - } - last = -1; + if (lastReturned < 0) throw new IllegalStateException(); + ActivityArrayMap.this.removeAtIndex(lastReturned); + index = lastReturned; + lastReturned = -1; } }; } @@ -275,9 +293,10 @@ public final class ActivityArrayMap implements Map { @Override public boolean contains(Object o) { if (o instanceof Entry entry && entry.getKey() instanceof Activity activity) { - @SuppressWarnings("unchecked") - V value = (V) a[activity.id]; - return Objects.equals(value, entry.getValue()); + int index = findIndex(activity.id); + if (index >= 0) { + return Objects.equals(v[index], entry.getValue()); + } } return false; } @@ -307,8 +326,8 @@ public final class ActivityArrayMap implements Map { @Override public int hashCode() { int hash = 0; - for (Entry entry : entrySet()) { - hash += entry.hashCode(); + for (int i = 0; i < size; i++) { + hash += Objects.hashCode(k[i]) ^ Objects.hashCode(v[i]); } return hash; } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/ActivityBitSet.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/ActivityBitSet.java index 975f2250..ebe7ca06 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/ActivityBitSet.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/ActivityBitSet.java @@ -1,22 +1,32 @@ package org.dreeam.leaf.util.map; +import it.unimi.dsi.fastutil.objects.AbstractObjectSet; +import it.unimi.dsi.fastutil.objects.ObjectIterator; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.entity.schedule.Activity; +import org.dreeam.leaf.util.RegistryTypeManager; import org.jetbrains.annotations.NotNull; import java.util.*; -public final class ActivityBitSet extends AbstractCollection implements Set { - public static final int BITS = 25; +public final class ActivityBitSet extends AbstractObjectSet { public int bitset = 0; + private boolean dirty = true; - public ActivityBitSet() { - if (BuiltInRegistries.ACTIVITY.size() != BITS + 1) { - throw new IllegalStateException("Unexpected registry minecraft:activity size"); + public boolean unsetDirty() { + if (dirty) { + dirty = false; + return true; + } else { + return false; } } + public int bitSet() { + return bitset; + } + private static Activity map(int i) { return BuiltInRegistries.ACTIVITY.byIdOrThrow(i); } @@ -26,6 +36,7 @@ public final class ActivityBitSet extends AbstractCollection implement int mask = 1 << activity.id; if ((bitset & mask) != 0) return false; bitset |= mask; + dirty = true; return true; } @@ -35,6 +46,7 @@ public final class ActivityBitSet extends AbstractCollection implement int mask = 1 << activity.id; if ((bitset & mask) != 0) { bitset &= ~mask; + dirty = true; return true; } } @@ -47,27 +59,27 @@ public final class ActivityBitSet extends AbstractCollection implement } @Override - public @NotNull Iterator iterator() { - return new Iterator<>() { + public @NotNull ObjectIterator iterator() { + return new ObjectIterator<>() { private int index = 0; - - private void advance() { - while (index < BITS && (bitset & (1 << index)) == 0) index++; - } { - advance(); + while (index < RegistryTypeManager.ACTIVITY_SIZE && (bitset & (1 << index)) == 0) { + index++; + } } @Override public boolean hasNext() { - return index < BITS; + return index < RegistryTypeManager.ACTIVITY_SIZE; } @Override public Activity next() { if (!hasNext()) throw new NoSuchElementException(); Activity act = map(index++); - advance(); + while (index < RegistryTypeManager.ACTIVITY_SIZE && (bitset & (1 << index)) == 0) { + index++; + } return act; } }; @@ -81,6 +93,7 @@ public final class ActivityBitSet extends AbstractCollection implement @Override public void clear() { bitset = 0; + dirty = true; } @Override @@ -94,7 +107,7 @@ public final class ActivityBitSet extends AbstractCollection implement @Override public int hashCode() { int hash = 0; - for (int i = 0; i < BITS; i++) { + for (int i = 0; i < RegistryTypeManager.ACTIVITY_SIZE; i++) { if ((bitset & (1 << i)) != 0) { hash += map(i).hashCode(); } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java index a99a1bb0..1c36e80d 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java @@ -15,7 +15,7 @@ import java.util.AbstractMap.SimpleEntry; public final class AttributeInstanceArrayMap implements Map, AttributeInstance>, Cloneable { private int size = 0; - private transient AttributeInstance[] a = new AttributeInstance[RegistryTypeManager.ATTRIBUTE_ID_COUNTER]; + private transient AttributeInstance[] a = new AttributeInstance[RegistryTypeManager.ATTRIBUTE_SIZE]; private transient KeySet keys; private transient Values values; private transient EntrySet entries; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/BehaviorControlArraySet.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/BehaviorControlArraySet.java index b3d85ee9..066e6b2f 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/BehaviorControlArraySet.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/BehaviorControlArraySet.java @@ -1,14 +1,26 @@ package org.dreeam.leaf.util.map; -import it.unimi.dsi.fastutil.objects.ObjectArraySet; +import it.unimi.dsi.fastutil.objects.AbstractObjectSet; +import it.unimi.dsi.fastutil.objects.ObjectIterator; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.ai.behavior.Behavior; import net.minecraft.world.entity.ai.behavior.BehaviorControl; +import org.jetbrains.annotations.NotNull; -public class BehaviorControlArraySet extends ObjectArraySet> { +import java.util.*; +import java.util.function.Consumer; + +public final class BehaviorControlArraySet extends AbstractObjectSet> { + + private static final BehaviorControl[] EMPTY_ARRAY = {}; private int running; + private transient BehaviorControl[] a; + private int size; - public Object[] raw() { + public BehaviorControlArraySet() { + this.a = EMPTY_ARRAY; + } + + public BehaviorControl[] raw() { return a; } @@ -23,4 +35,124 @@ public class BehaviorControlArraySet extends ObjectArray public boolean running() { return running != 0; } + + @Override + public int size() { + return size; + } + + private int findKey(final Object o) { + final BehaviorControl[] a = this.a; + for (int i = size; i-- != 0; ) if (Objects.equals(a[i], o)) return i; + return -1; + } + + @Override + public boolean isEmpty() { + return size == 0; + } + + @Override + public boolean contains(Object k) { + return findKey(k) != -1; + } + + @Override + public ObjectIterator> iterator() { + return new ObjectIterator<>() { + int curr = -1, next = 0; + + @Override + public boolean hasNext() { + return next < size; + } + + @Override + public BehaviorControl next() { + if (!hasNext()) throw new NoSuchElementException(); + return a[curr = next++]; + } + + @Override + public void remove() { + if (curr == -1) throw new IllegalStateException(); + curr = -1; + final int tail = size-- - next--; + System.arraycopy(a, next + 1, a, next, tail); + a[size] = null; + } + + @Override + public int skip(int n) { + if (n < 0) throw new IllegalArgumentException("Argument must be nonnegative: " + n); + n = Math.min(n, size - next); + next += n; + if (n != 0) curr = next - 1; + return n; + } + + @Override + public void forEachRemaining(final Consumer> action) { + final BehaviorControl[] a = BehaviorControlArraySet.this.a; + while (next < size) action.accept(a[next++]); + } + }; + } + + @Override + public @NotNull Object[] toArray() { + + final int size = size(); + if (size == 0) return it.unimi.dsi.fastutil.objects.ObjectArrays.EMPTY_ARRAY; + return java.util.Arrays.copyOf(a, size, Object[].class); + } + + @Override + public @NotNull T[] toArray(@NotNull T[] a) { + if (a == null) { + a = (T[]) new Object[size]; + } else if (a.length < size) { + a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size); + } + System.arraycopy(this.a, 0, a, 0, size); + if (a.length > size) { + a[size] = null; + } + return a; + } + + @Override + public boolean add(BehaviorControl k) { + final int pos = findKey(k); + if (pos != -1) return false; + if (size == a.length) { + final BehaviorControl[] b = new BehaviorControl[size == 0 ? 2 : size * 2]; + for (int i = size; i-- != 0; ) b[i] = a[i]; + a = b; + } + a[size++] = k; + return true; + } + + @Override + public boolean remove(Object k) { + final int pos = findKey(k); + if (pos == -1) return false; + final int tail = size - pos - 1; + for (int i = 0; i < tail; i++) a[pos + i] = a[pos + i + 1]; + size--; + a[size] = null; + return true; + } + + @Override + public void clear() { + java.util.Arrays.fill(a, 0, size, null); + size = 0; + } + + @Override + public boolean equals(Object o) { + return false; + } }