Skip to content

Commit

Permalink
Optimized ItemEntity stacking using item type categorization
Browse files Browse the repository at this point in the history
  • Loading branch information
2No2Name committed Nov 8, 2023
1 parent ec7ffab commit 3343935
Show file tree
Hide file tree
Showing 24 changed files with 499 additions and 53 deletions.
15 changes: 14 additions & 1 deletion lithium-mixin-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Mob AI optimizations
Event-based system for tracking nearby entities.

Requirements:
- `mixin.util.entity_section_position=true`
- `mixin.util.entity_section_position=true`
- `mixin.util.accessors=true`

### `mixin.ai.nearby_entity_tracking.goals`
(default: `true`)
Expand Down Expand Up @@ -178,6 +179,8 @@ Various world chunk optimizations
### `mixin.chunk.entity_class_groups`
(default: `true`)
Allow grouping entity classes for faster entity access, e.g. boats and shulkers
Requirements:
- `mixin.util.accessors=true`

### `mixin.chunk.no_locking`
(default: `true`)
Expand Down Expand Up @@ -309,6 +312,12 @@ Hopper minecarts search for item entities faster by combining multiple item enti
(default: `true`)
Block updates skip notifying mobs that won't react to the block update anyways

### `mixin.entity.item_entity_stacking`
(default: `true`)
Optimize item entity stacking by categorizing item entities by item type and only attempting to merge with the same type.
Requirements:
- `mixin.util.accessors=true`

### `mixin.entity.replace_entitytype_predicates`
(default: `true`)
Accesses entities of the correct type directly instead of accessing all nearby entities and filtering them afterwards
Expand Down Expand Up @@ -431,6 +440,10 @@ Specialized VoxelShape implementations are used for cuboid and empty shapes. Col
(default: `true`)
Various utilities for other mixins

### `mixin.util.accessors`
(default: `true`)
Allow accessing certain fields and functions that are normally inaccessible

### `mixin.util.block_entity_retrieval`
(default: `true`)
Allows access to existing BlockEntities without creating new ones
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package me.jellysquid.mods.lithium.common.entity;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public interface TypeFilterableListInternalAccess<T> {
<S extends T> List<S> lithium$getOrCreateAllOfTypeRaw(Class<S> type);

<S extends T> List<S> lithium$replaceCollectionAndGet(Class<S> type, Function<ArrayList<S>, List<S>> listCtor);
<S extends T> List<S> lithium$replaceCollectionAndGet(Class<S> type, ArrayList<S> list);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package me.jellysquid.mods.lithium.common.entity.item;

import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import me.jellysquid.mods.lithium.mixin.util.accessors.ItemStackAccessor;
import net.minecraft.entity.ItemEntity;
import net.minecraft.item.Item;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

public class ItemEntityCategorizingList extends AbstractList<ItemEntity> {
public static final int ITEM_ENTITY_CATEGORIZATION_THRESHOLD = 20;

private final ArrayList<ItemEntity> delegate;
private Reference2ReferenceOpenHashMap<Item, ArrayList<ItemEntity>> itemEntitiesByItem;

public ItemEntityCategorizingList(ArrayList<ItemEntity> delegate) {
this.delegate = delegate;
}

public List<ItemEntity> getItemEntitiesForMerge(ItemEntity searchingEntity) {
if (this.itemEntitiesByItem == null) {
this.itemEntitiesByItem = new Reference2ReferenceOpenHashMap<>();
for (ItemEntity entity : this.delegate) {
this.itemEntitiesByItem.computeIfAbsent(((ItemStackAccessor) (Object) entity.getStack()).lithium$getItem(), item -> new ArrayList<>()).add(entity);
}
}

ArrayList<ItemEntity> itemEntities = this.itemEntitiesByItem.get(((ItemStackAccessor) (Object) searchingEntity.getStack()).lithium$getItem());
if (itemEntities == null) {
return Collections.emptyList();
}
return itemEntities;
}


public void invalidateCache() {
this.itemEntitiesByItem = null;
}

public ArrayList<ItemEntity> getDelegate() {
this.itemEntitiesByItem = null;
return this.delegate;
}


@Override
public int size() {
return delegate.size();
}

@Override
public boolean isEmpty() {
return delegate.isEmpty();
}

@Override
public boolean contains(Object o) {
return delegate.contains(o);
}

@NotNull
@Override
public Iterator<ItemEntity> iterator() {
this.itemEntitiesByItem = null;
return delegate.iterator();
}

@NotNull
@Override
public Object[] toArray() {
return delegate.toArray();
}

@NotNull
@Override
public <T> T[] toArray(@NotNull T[] a) {
return delegate.toArray(a);
}

@Override
public boolean add(ItemEntity itemEntity) {
if (this.itemEntitiesByItem != null) {
Item category = ((ItemStackAccessor) (Object) itemEntity.getStack()).lithium$getItem();
this.itemEntitiesByItem.computeIfAbsent(category, item -> new ArrayList<>()).add(itemEntity);
}
return delegate.add(itemEntity);
}

@Override
public boolean remove(Object o) {
if (o instanceof ItemEntity itemEntity && this.itemEntitiesByItem != null) {
Item category = ((ItemStackAccessor) (Object) itemEntity.getStack()).lithium$getItem();

ArrayList<ItemEntity> itemEntities = this.itemEntitiesByItem.get(category);
if (itemEntities != null) {
itemEntities.remove(itemEntity);
}
}
return delegate.remove(o);
}

@Override
public boolean containsAll(@NotNull Collection<?> c) {
return delegate.containsAll(c);
}

@Override
public boolean addAll(@NotNull Collection<? extends ItemEntity> c) {
for (ItemEntity itemEntity : c) {
this.add(itemEntity);
}
return delegate.addAll(c);
}

@Override
public boolean addAll(int index, @NotNull Collection<? extends ItemEntity> c) {
this.itemEntitiesByItem = null;
return delegate.addAll(index, c);
}

@Override
public boolean removeAll(@NotNull Collection<?> c) {
this.itemEntitiesByItem = null;
return delegate.removeAll(c);
}

@Override
public boolean retainAll(@NotNull Collection<?> c) {
this.itemEntitiesByItem = null;
return delegate.retainAll(c);
}

@Override
public void replaceAll(UnaryOperator<ItemEntity> operator) {
this.itemEntitiesByItem = null;
delegate.replaceAll(operator);
}

@Override
public void sort(Comparator<? super ItemEntity> c) {
this.itemEntitiesByItem = null;
delegate.sort(c);
}

@Override
public void clear() {
this.itemEntitiesByItem = null;
delegate.clear();
}

@Override
public boolean equals(Object o) {
return delegate.equals(o);
}

@Override
public int hashCode() {
return delegate.hashCode();
}

@Override
public ItemEntity get(int index) {
return delegate.get(index);
}

@Override
public ItemEntity set(int index, ItemEntity element) {
this.itemEntitiesByItem = null;
return delegate.set(index, element);
}

@Override
public void add(int index, ItemEntity element) {
this.itemEntitiesByItem = null;
delegate.add(index, element);
}

@Override
public ItemEntity remove(int index) {
ItemEntity remove = delegate.remove(index);
if (this.itemEntitiesByItem != null) {
Item category = ((ItemStackAccessor) (Object) remove.getStack()).lithium$getItem();
ArrayList<ItemEntity> itemEntities = this.itemEntitiesByItem.get(category);
if (itemEntities != null) {
itemEntities.remove(remove);
}
}
return remove;
}

@Override
public int indexOf(Object o) {
return delegate.indexOf(o);
}

@Override
public int lastIndexOf(Object o) {
return delegate.lastIndexOf(o);
}

@NotNull
@Override
public ListIterator<ItemEntity> listIterator() {
this.itemEntitiesByItem = null;
return delegate.listIterator();
}

@NotNull
@Override
public ListIterator<ItemEntity> listIterator(int index) {
this.itemEntitiesByItem = null;
return delegate.listIterator(index);
}

@NotNull
@Override
public List<ItemEntity> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException();
}

@Override
public Spliterator<ItemEntity> spliterator() {
return delegate.spliterator();
}

@Override
public <T> T[] toArray(IntFunction<T[]> generator) {
return delegate.toArray(generator);
}

@Override
public boolean removeIf(Predicate<? super ItemEntity> filter) {
this.itemEntitiesByItem = null;
return delegate.removeIf(filter);
}

@Override
public Stream<ItemEntity> stream() {
return delegate.stream();
}

@Override
public Stream<ItemEntity> parallelStream() {
return delegate.parallelStream();
}

@Override
public void forEach(Consumer<? super ItemEntity> action) {
delegate.forEach(action);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

public interface ItemStackSubscriber {

void notifyBeforeCountChange(int slot, int newCount);
void lithium$notifyBeforeCountChange(int slot, int newCount);
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ public int getSlot(ItemStackSubscriber subscriber) {
}

@Override
public void notifyBeforeCountChange(int slot, int newCount) {
public void lithium$notifyBeforeCountChange(int slot, int newCount) {
ItemStackSubscriber[] itemStackSubscribers = this.subscribers;
for (int i = 0; i < itemStackSubscribers.length; i++) {
ItemStackSubscriber subscriber = itemStackSubscribers[i];
subscriber.notifyBeforeCountChange(this.slots[i], newCount);
subscriber.lithium$notifyBeforeCountChange(this.slots[i], newCount);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package me.jellysquid.mods.lithium.common.entity.nearby_tracker;

import me.jellysquid.mods.lithium.common.util.tuples.Range6Int;
import me.jellysquid.mods.lithium.mixin.ai.nearby_entity_tracking.ServerEntityManagerAccessor;
import me.jellysquid.mods.lithium.mixin.ai.nearby_entity_tracking.ServerWorldAccessor;
import me.jellysquid.mods.lithium.mixin.util.accessors.ServerEntityManagerAccessor;
import me.jellysquid.mods.lithium.mixin.util.accessors.ServerWorldAccessor;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.ChunkSectionPos;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public void changedALot() {

}

public void notifyBeforeCountChange(int slot, int newCount) {
public void lithium$notifyBeforeCountChange(int slot, int newCount) {
ItemStack stack = this.get(slot);
int count = stack.getCount();
if (newCount <= 0) {
Expand Down
Loading

0 comments on commit 3343935

Please sign in to comment.