Avoid doing unnecessary range checks when we're looping from start to end.

Make EntityLiving call AI logic every tick again.
Rework PathfinderGoalSelector logic.

Adds UnsafeList for use in places where we use ArrayList and know we won't
get index out of range errors. Added usage to World's tickEntities, Chunk's
entitySlices to speed up searching for entities, and to PathfinderGoalSelector
to speed up dealing with AI goals.

Reworked logic in PathfinderGoalSelector with help from fullwall. This code
no longer uses an extra ArrayList for setting up goals and only updates which
goals should be run every other time it is called.

Removed only calling PathfinderGoalSelector every other tick from EntityLiving
as we now only setup new goals every other tick. This ensures existing goals
run every tick to properly update mob movement.

By: Travis Watkins <amaranth@ubuntu.com>
This commit is contained in:
CraftBukkit/Spigot 2012-04-23 09:47:05 -05:00
parent db27daeea7
commit f977f3cc65

View File

@ -0,0 +1,154 @@
package org.bukkit.craftbukkit.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.List;
import java.util.RandomAccess;
// implementation of an ArrayList that offers a getter without range checks
public class UnsafeList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
private static final long serialVersionUID = 8683452581112892189L;
private transient Object[] data;
private int size;
public UnsafeList(int capacity) {
super();
if (capacity < 0) capacity = 128;
int rounded = Integer.highestOneBit(capacity - 1) << 1;
data = new Object[rounded];
}
public UnsafeList() {
this(128);
}
public E get(int index) {
rangeCheck(index);
return (E) data[index];
}
public E unsafeGet(int index) {
return (E) data[index];
}
public E set(int index, E element) {
rangeCheck(index);
E old = (E) data[index];
data[index] = element;
return old;
}
public boolean add(E element) {
growIfNeeded();
data[size++] = element;
return true;
}
public void add(int index, E element) {
growIfNeeded();
System.arraycopy(data, index, data, index + 1, size - index);
data[index] = element;
size++;
}
public E remove(int index) {
rangeCheck(index);
E old = (E) data[index];
int movedCount = size - index - 1;
if (movedCount > 0) {
System.arraycopy(data, index + 1, data, index, movedCount);
}
data[--size] = null;
return old;
}
public boolean remove(Object o) {
int index = indexOf(o);
if (index >= 0) {
remove(index);
return true;
}
return false;
}
public int indexOf(Object o) {
for (int i = 0; i < size; i++) {
if (o == data[i] || o.equals(data[i])) {
return i;
}
}
return -1;
}
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public void clear() {
for (int i = 0; i < size; i++) {
data[i] = null;
}
size = 0;
}
// actually rounds up to nearest power of two
public void trimToSize() {
int old = data.length;
int rounded = Integer.highestOneBit(size - 1) << 1;
if (rounded < old) {
data = Arrays.copyOf(data, rounded);
}
}
public int size() {
return size;
}
public boolean isEmpty() {
return size != 0;
}
private void rangeCheck(int index) {
if (index >= size || index < 0) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
}
private void growIfNeeded() {
if (size == data.length) {
Object[] newData = new Object[data.length << 1];
System.arraycopy(data, 0, newData, 0, size);
data = newData;
}
}
private void writeObject(ObjectOutputStream os) throws IOException {
os.defaultWriteObject();
os.writeInt(size);
for (int i = 0; i < size; i++) {
os.writeObject(data[i]);
}
}
private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
is.defaultReadObject();
size = is.readInt();
data = new Object[Integer.highestOneBit(size - 1) << 1];
for (int i = 0; i < size; i++) {
data[i] = is.readObject();
}
}
}