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.
This commit is contained in:
Travis Watkins 2012-04-23 09:47:05 -05:00 committed by EvilSeph
parent e8459c1ff6
commit b7376e0865
5 changed files with 207 additions and 44 deletions

View File

@ -8,6 +8,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import org.bukkit.Bukkit; // CraftBukkit import org.bukkit.Bukkit; // CraftBukkit
import org.bukkit.craftbukkit.util.UnsafeList; // CraftBukkit
public class Chunk { public class Chunk {
@ -53,7 +54,7 @@ public class Chunk {
this.heightMap = new int[256]; this.heightMap = new int[256];
for (int k = 0; k < this.entitySlices.length; ++k) { for (int k = 0; k < this.entitySlices.length; ++k) {
this.entitySlices[k] = new ArrayList(); this.entitySlices[k] = new UnsafeList(); // CraftBukkit - use UnsafeList
} }
Arrays.fill(this.b, -999); Arrays.fill(this.b, -999);
@ -697,10 +698,10 @@ public class Chunk {
} }
for (int k = i; k <= j; ++k) { for (int k = i; k <= j; ++k) {
List list1 = this.entitySlices[k]; UnsafeList list1 = (UnsafeList) this.entitySlices[k]; // CraftBukkit - use UnsafeList
for (int l = 0; l < list1.size(); ++l) { for (int l = 0; l < list1.size(); ++l) {
Entity entity1 = (Entity) list1.get(l); Entity entity1 = (Entity) list1.unsafeGet(l); // CraftBukkit - use unsafeGet
if (entity1 != entity && entity1.boundingBox.a(axisalignedbb)) { if (entity1 != entity && entity1.boundingBox.a(axisalignedbb)) {
list.add(entity1); list.add(entity1);
@ -736,10 +737,10 @@ public class Chunk {
} }
for (int k = i; k <= j; ++k) { for (int k = i; k <= j; ++k) {
List list1 = this.entitySlices[k]; UnsafeList list1 = (UnsafeList) this.entitySlices[k]; // CraftBukkit - use UnsafeList
for (int l = 0; l < list1.size(); ++l) { for (int l = 0; l < list1.size(); ++l) {
Entity entity = (Entity) list1.get(l); Entity entity = (Entity) list1.unsafeGet(l); // CraftBukkit - use unsafeGet
if (oclass.isAssignableFrom(entity.getClass()) && entity.boundingBox.a(axisalignedbb)) { if (oclass.isAssignableFrom(entity.getClass()) && entity.boundingBox.a(axisalignedbb)) {
list.add(entity); list.add(entity);

View File

@ -1095,22 +1095,22 @@ public abstract class EntityLiving extends Entity {
d1 += 1.0D; d1 += 1.0D;
this.setPosition(d0, d1, d2); this.setPosition(d0, d1, d2);
} }
//List list = this.world.getCubes(this, this.boundingBox.shrink(0.03125D, 0.0D, 0.03125D)); /*List list = this.world.getCubes(this, this.boundingBox.shrink(0.03125D, 0.0D, 0.03125D));
//if (list.size() > 0) { if (list.size() > 0) {
// double d4 = 0.0D; double d4 = 0.0D;
// for (int i = 0; i < list.size(); ++i) { for (int i = 0; i < list.size(); ++i) {
// AxisAlignedBB axisalignedbb = (AxisAlignedBB) list.get(i); AxisAlignedBB axisalignedbb = (AxisAlignedBB) list.get(i);
// if (axisalignedbb.e > d4) { if (axisalignedbb.e > d4) {
// d4 = axisalignedbb.e; d4 = axisalignedbb.e;
// } }
// } }
// d1 += d4 - this.boundingBox.b; d1 += d4 - this.boundingBox.b;
// this.setPosition(d0, d1, d2); this.setPosition(d0, d1, d2);
//} }*/
// CraftBukkit end // CraftBukkit end
} }
@ -1240,14 +1240,10 @@ public abstract class EntityLiving extends Entity {
this.m.a(); this.m.a();
//MethodProfiler.a(); // CraftBukkit - not in production code //MethodProfiler.a(); // CraftBukkit - not in production code
//MethodProfiler.a("targetSelector"); // CraftBukkit - not in production code //MethodProfiler.a("targetSelector"); // CraftBukkit - not in production code
// CraftBukkit start - run goal selectors every other tick
if ((this.aV & 1) == 0) {
this.targetSelector.a(); this.targetSelector.a();
//MethodProfiler.a(); // CraftBukkit - not in production code //MethodProfiler.a(); // CraftBukkit - not in production code
//MethodProfiler.a("goalSelector"); // CraftBukkit - not in production code //MethodProfiler.a("goalSelector"); // CraftBukkit - not in production code
this.goalSelector.a(); this.goalSelector.a();
}
// CraftBukkit end
//MethodProfiler.a(); // CraftBukkit - not in production code //MethodProfiler.a(); // CraftBukkit - not in production code
//MethodProfiler.a("navigation"); // CraftBukkit - not in production code //MethodProfiler.a("navigation"); // CraftBukkit - not in production code
this.navigation.d(); this.navigation.d();

View File

@ -2,12 +2,15 @@ package net.minecraft.server;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import org.bukkit.craftbukkit.util.UnsafeList; // CraftBukkit
public class PathfinderGoalSelector { public class PathfinderGoalSelector {
private ArrayList a = new ArrayList(); // CraftBukkit start - use UnsafeList, add boolean for picking goals
private ArrayList b = new ArrayList(); private UnsafeList a = new UnsafeList(16);
private ArrayList arraylist = new ArrayList(); // CraftBukkit - create list for object reuse private UnsafeList b = new UnsafeList(16);
boolean oddCall = true;
// CraftBukkit end
public PathfinderGoalSelector() {} public PathfinderGoalSelector() {}
@ -16,12 +19,12 @@ public class PathfinderGoalSelector {
} }
public void a() { public void a() {
//ArrayList arraylist = new ArrayList(); // CraftBukkit //ArrayList arraylist = new ArrayList(); // CraftBukkit - removed usage
arraylist.clear(); // CraftBukkit - prepare reused list
// CraftBukkit start - don't use iterators for private fields // CraftBukkit start - don't use iterators for private fields, only run every other call
if (oddCall) {
for (int i = 0; i < this.a.size(); i++) { for (int i = 0; i < this.a.size(); i++) {
PathfinderGoalSelectorItem pathfindergoalselectoritem = (PathfinderGoalSelectorItem) this.a.get(i); PathfinderGoalSelectorItem pathfindergoalselectoritem = (PathfinderGoalSelectorItem) this.a.unsafeGet(i); // CraftBukkit - use unsafeGet
boolean flag = this.b.contains(pathfindergoalselectoritem); boolean flag = this.b.contains(pathfindergoalselectoritem);
if (flag) { if (flag) {
@ -34,36 +37,44 @@ public class PathfinderGoalSelector {
} }
if (this.a(pathfindergoalselectoritem) && pathfindergoalselectoritem.a.a()) { if (this.a(pathfindergoalselectoritem) && pathfindergoalselectoritem.a.a()) {
arraylist.add(pathfindergoalselectoritem); // CraftBukkit start - call method now
// arraylist.add(pathfindergoalselectoritem);
pathfindergoalselectoritem.a.c();
// CraftBukkit end
this.b.add(pathfindergoalselectoritem); this.b.add(pathfindergoalselectoritem);
} }
} }
}
oddCall = !oddCall;
// CraftBukkit end // CraftBukkit end
boolean flag1 = false; boolean flag1 = false;
if (flag1 && arraylist.size() > 0) { // CraftBukkit start - removed usage of arraylist
/*if (flag1 && arraylist.size() > 0) {
System.out.println("Starting: "); System.out.println("Starting: ");
} }*/
// CraftBukkit end
Iterator iterator1; Iterator iterator1;
PathfinderGoalSelectorItem pathfindergoalselectoritem1; PathfinderGoalSelectorItem pathfindergoalselectoritem1;
//CraftBukkit start - don't use iterators for private fields // CraftBukkit start - removed usage of arraylist
for (int i = 0; i < arraylist.size(); i++) { /*for (iterator1 = arraylist.iterator(); iterator1.hasNext(); pathfindergoalselectoritem1.a.c()) {
pathfindergoalselectoritem1 = (PathfinderGoalSelectorItem) arraylist.get(i); pathfindergoalselectoritem1 = (PathfinderGoalSelectorItem) iterator1.next();
pathfindergoalselectoritem1.a.c();
if (flag1) { if (flag1) {
System.out.println(pathfindergoalselectoritem1.a.toString() + ", "); System.out.println(pathfindergoalselectoritem1.a.toString() + ", ");
} }
} }*/
// CraftBukkit end
if (flag1 && this.b.size() > 0) { if (flag1 && this.b.size() > 0) {
System.out.println("Running: "); System.out.println("Running: ");
} }
// CraftBukkit start - don't use iterators for private fields
for (int i = 0; i < this.b.size(); i++) { for (int i = 0; i < this.b.size(); i++) {
pathfindergoalselectoritem1 = (PathfinderGoalSelectorItem) this.b.get(i); pathfindergoalselectoritem1 = (PathfinderGoalSelectorItem) this.b.unsafeGet(i); // CraftBukkit - use unsafeGet
pathfindergoalselectoritem1.a.e(); pathfindergoalselectoritem1.a.e();
if (flag1) { if (flag1) {
System.out.println(pathfindergoalselectoritem1.a.toString()); System.out.println(pathfindergoalselectoritem1.a.toString());
@ -75,14 +86,14 @@ public class PathfinderGoalSelector {
private boolean a(PathfinderGoalSelectorItem pathfindergoalselectoritem) { private boolean a(PathfinderGoalSelectorItem pathfindergoalselectoritem) {
// CraftBukkit start - don't use iterators for private fields // CraftBukkit start - don't use iterators for private fields
for (int i = 0; i < this.a.size(); i++) { for (int i = 0; i < this.a.size(); i++) {
PathfinderGoalSelectorItem pathfindergoalselectoritem1 = (PathfinderGoalSelectorItem) this.a.get(i); PathfinderGoalSelectorItem pathfindergoalselectoritem1 = (PathfinderGoalSelectorItem) this.a.unsafeGet(i); // CraftBukkit - use unsafeGet
if (pathfindergoalselectoritem1 != pathfindergoalselectoritem) { if (pathfindergoalselectoritem1 != pathfindergoalselectoritem) {
if (pathfindergoalselectoritem.b >= pathfindergoalselectoritem1.b) { if (pathfindergoalselectoritem.b >= pathfindergoalselectoritem1.b) {
if (this.b.contains(pathfindergoalselectoritem1) && !this.a(pathfindergoalselectoritem, pathfindergoalselectoritem1)) { if (!this.a(pathfindergoalselectoritem, pathfindergoalselectoritem1) && this.b.contains(pathfindergoalselectoritem1)) { // CraftBukkit - switch order
return false; return false;
} }
} else if (this.b.contains(pathfindergoalselectoritem1) && !pathfindergoalselectoritem1.a.g()) { } else if (!pathfindergoalselectoritem1.a.g() && this.b.contains(pathfindergoalselectoritem1)) { // CraftBukkit - switch order
return false; return false;
} }
} }

View File

@ -20,6 +20,7 @@ import org.bukkit.generator.ChunkGenerator;
import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.craftbukkit.util.UnsafeList;
import org.bukkit.event.block.BlockCanBuildEvent; import org.bukkit.event.block.BlockCanBuildEvent;
import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.block.BlockFormEvent;
@ -40,7 +41,7 @@ public class World implements IBlockAccess {
private List J = new ArrayList(); private List J = new ArrayList();
private List K = new ArrayList(); private List K = new ArrayList();
public List players = new ArrayList(); public List players = new ArrayList();
public List e = new ArrayList(); public UnsafeList e = new UnsafeList(); // CraftBukkit - use UnsafeList
private long L = 16777215L; private long L = 16777215L;
public int f = 0; public int f = 0;
protected int g = (new Random()).nextInt(); protected int g = (new Random()).nextInt();
@ -1104,7 +1105,7 @@ public class World implements IBlockAccess {
Entity entity; Entity entity;
for (i = 0; i < this.e.size(); ++i) { for (i = 0; i < this.e.size(); ++i) {
entity = (Entity) this.e.get(i); entity = (Entity) this.e.unsafeGet(i); // CraftBukkit - use unsafeGet
// CraftBukkit start - fixed an NPE // CraftBukkit start - fixed an NPE
if (entity == null) { if (entity == null) {
continue; continue;

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();
}
}
}