Solve most race issue around the move(), making world hole less likely
This commit is contained in:
@@ -39,6 +39,8 @@ import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.MovabeGridRingList;
|
||||
import com.seibel.lod.core.util.MovabeGridRingList.Pos;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
@@ -75,7 +77,7 @@ public class LodDimension
|
||||
// these three variables are private to force use of the getWidth() method
|
||||
// which is a safer way to get the width then directly asking the arrays
|
||||
/** stores all the regions in this dimension */
|
||||
public volatile LodRegion[][] regions;
|
||||
public MovabeGridRingList<LodRegion> regions;
|
||||
//NOTE: This list pos is relative to center
|
||||
private volatile RegionPos[] iteratorList = null;
|
||||
|
||||
@@ -91,8 +93,6 @@ public class LodDimension
|
||||
|
||||
private LodDimensionFileHandler fileHandler;
|
||||
|
||||
private final RegionPos center;
|
||||
|
||||
public volatile int dirtiedRegionsRoughCount = 0;
|
||||
|
||||
private boolean isCutting = false;
|
||||
@@ -143,9 +143,7 @@ public class LodDimension
|
||||
}
|
||||
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
|
||||
center = new RegionPos(0, 0);
|
||||
regions = new MovabeGridRingList<LodRegion>(halfWidth, 0, 0);
|
||||
generateIteratorList();
|
||||
}
|
||||
|
||||
@@ -181,91 +179,8 @@ public class LodDimension
|
||||
{
|
||||
ClientApi.LOGGER.info("LodDim MOVE. Offset: "+regionOffset);
|
||||
saveDirtyRegionsToFile(false); //async add dirty regions to be saved.
|
||||
int xOffset = regionOffset.x;
|
||||
int zOffset = regionOffset.z;
|
||||
|
||||
// if the x or z offset is equal to or greater than
|
||||
// the total width, just delete the current data
|
||||
// and update the centerX and/or centerZ
|
||||
if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
for (int z = 0; z < width; z++) {
|
||||
regions[x][z] = null;
|
||||
}
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// X
|
||||
if (xOffset > 0)
|
||||
{
|
||||
// move everything over to the left (as the center moves to the right)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
if (x + xOffset < width)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move everything over to the right (as the center moves to the left)
|
||||
for (int x = width - 1; x >= 0; x--)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
if (x + xOffset >= 0)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Z
|
||||
if (zOffset > 0)
|
||||
{
|
||||
// move everything up (as the center moves down)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
if (z + zOffset < width)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move everything down (as the center moves up)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = width - 1; z >= 0; z--)
|
||||
{
|
||||
if (z + zOffset >= 0)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
Pos p = regions.getCenter();
|
||||
regions.move(p.x+regionOffset.x, p.y+regionOffset.z);
|
||||
ClientApi.LOGGER.info("LodDim MOVE complete. Offset: "+regionOffset);
|
||||
}
|
||||
|
||||
@@ -280,19 +195,13 @@ public class LodDimension
|
||||
{
|
||||
int xRegion = LevelPosUtil.getRegion(detailLevel, levelPosX);
|
||||
int zRegion = LevelPosUtil.getRegion(detailLevel, levelPosZ);
|
||||
int xIndex = (xRegion - center.x) + halfWidth;
|
||||
int zIndex = (zRegion - center.z) + halfWidth;
|
||||
LodRegion region = regions.get(xRegion, zRegion);
|
||||
|
||||
if (!regionIsInRange(xRegion, zRegion))
|
||||
return null;
|
||||
// throw new ArrayIndexOutOfBoundsException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " out of range");
|
||||
else if (regions[xIndex][zIndex] == null)
|
||||
return null;
|
||||
else if (regions[xIndex][zIndex].getMinDetailLevel() > detailLevel)
|
||||
if (region != null && region.getMinDetailLevel() > detailLevel)
|
||||
return null;
|
||||
//throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel());
|
||||
|
||||
return regions[xIndex][zIndex];
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,37 +212,29 @@ public class LodDimension
|
||||
*/
|
||||
public LodRegion getRegion(int regionPosX, int regionPosZ)
|
||||
{
|
||||
int xIndex = (regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(regionPosX, regionPosZ))
|
||||
return null;
|
||||
//throw new ArrayIndexOutOfBoundsException("Region " + regionPosX + " " + regionPosZ + " out of range");
|
||||
|
||||
return regions[xIndex][zIndex];
|
||||
return regions.get(regionPosX, regionPosZ);
|
||||
}
|
||||
|
||||
/** Useful when iterating over every region. */
|
||||
@Deprecated
|
||||
public LodRegion getRegionByArrayIndex(int xIndex, int zIndex)
|
||||
{
|
||||
return regions[xIndex][zIndex];
|
||||
Pos p = regions.getMinInRange();
|
||||
return regions.get(p.x+xIndex, p.y+zIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the LodRegion at the location of newRegion with newRegion.
|
||||
* @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension.
|
||||
*/
|
||||
public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
|
||||
/*public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
|
||||
{
|
||||
int xIndex = (newRegion.regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (newRegion.regionPosZ - center.z) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ))
|
||||
// out of range
|
||||
throw new ArrayIndexOutOfBoundsException("Region " + newRegion.regionPosX + ", " + newRegion.regionPosZ + " out of range");
|
||||
|
||||
regions[xIndex][zIndex] = newRegion;
|
||||
}
|
||||
}*/
|
||||
|
||||
public interface PosComsumer {
|
||||
void run(int x, int z);
|
||||
}
|
||||
@@ -342,7 +243,7 @@ public class LodDimension
|
||||
int ox,oy,dx,dy;
|
||||
ox = oy = dx = 0;
|
||||
dy = -1;
|
||||
int len = regions.length;
|
||||
int len = regions.getSize();
|
||||
int maxI = len*len;
|
||||
int halfLen = len/2;
|
||||
for(int i =0; i < maxI; i++){
|
||||
@@ -384,20 +285,18 @@ public class LodDimension
|
||||
Runnable thread = () -> {
|
||||
//ClientApi.LOGGER.info("LodDim cut Region: " + playerPosX + "," + playerPosZ);
|
||||
totalDirtiedRegions = 0;
|
||||
Pos minPos = regions.getMinInRange();
|
||||
// go over every region in the dimension
|
||||
iterateWithSpiral((int x, int z) -> {
|
||||
int regionX;
|
||||
int regionZ;
|
||||
int minDistance;
|
||||
byte detail;
|
||||
regionX = (x + center.x) - halfWidth;
|
||||
regionZ = (z + center.z) - halfWidth;
|
||||
LodRegion region = regions[x][z];
|
||||
|
||||
LodRegion region = regions.get(x+minPos.x, z+minPos.y);
|
||||
if (region != null && region.needSaving) totalDirtiedRegions++;
|
||||
if (region != null && !region.needSaving && region.isWriting==0) {
|
||||
// check what detail level this region should be
|
||||
// and cut it if it is higher then that
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ,
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, x+minPos.x, z+minPos.y,
|
||||
playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
|
||||
if (region.getMinDetailLevel() < detail) {
|
||||
@@ -435,6 +334,7 @@ public class LodDimension
|
||||
Runnable thread = () -> {
|
||||
//ClientApi.LOGGER.info("LodDim expend Region: " + playerPosX + "," + playerPosZ);
|
||||
|
||||
Pos minPos = regions.getMinInRange();
|
||||
iterateWithSpiral((int x, int z) -> {
|
||||
int regionX;
|
||||
int regionZ;
|
||||
@@ -443,10 +343,10 @@ public class LodDimension
|
||||
int maxDistance;
|
||||
byte minDetail;
|
||||
byte maxDetail;
|
||||
regionX = (x + center.x) - halfWidth;
|
||||
regionZ = (z + center.z) - halfWidth;
|
||||
regionX = x + minPos.x;
|
||||
regionZ = z + minPos.y;
|
||||
final RegionPos regionPos = new RegionPos(regionX, regionZ);
|
||||
region = regions[x][z];
|
||||
region = regions.get(regionX, regionZ);
|
||||
if (region != null && region.isWriting!=0) return; // FIXME: A crude attempt at lowering chance of race condition!
|
||||
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX,
|
||||
@@ -459,13 +359,13 @@ public class LodDimension
|
||||
boolean updated = false;
|
||||
if (region == null) {
|
||||
region = getRegionFromFile(regionPos, minDetail, verticalQuality);
|
||||
regions[x][z] = region;
|
||||
regions.set(regionX, regionZ, region);
|
||||
updated = true;
|
||||
} else if (region.getVerticalQuality() != verticalQuality ||
|
||||
region.getMinDetailLevel() > minDetail) {
|
||||
// The 'getRegionFromFile' will flush and save the region if it returns a new one
|
||||
region = getRegionFromFile(regions[x][z], minDetail, verticalQuality);
|
||||
regions[x][z] = region;
|
||||
region = getRegionFromFile(region, minDetail, verticalQuality);
|
||||
regions.set(regionX, regionZ, region);
|
||||
updated = true;
|
||||
} else if (minDetail <= dropoffSwitch && region.lastMaxDetailLevel != maxDetail) {
|
||||
region.lastMaxDetailLevel = maxDetail;
|
||||
@@ -531,12 +431,12 @@ public class LodDimension
|
||||
// This ensures that we don't spawn way too much regions without finish flushing them first.
|
||||
if (dirtiedRegionsRoughCount > 16) return posToGenerate;
|
||||
GenerationPriority allowedPriority = dirtiedRegionsRoughCount>12 ? GenerationPriority.NEAR_FIRST : priority;
|
||||
|
||||
Pos minPos = regions.getMinInRange();
|
||||
iterateByDistance((int x, int z) -> {
|
||||
boolean isCloseRange = (Math.abs(x-halfWidth)+Math.abs(z-halfWidth)<=2);
|
||||
//boolean isCloseRange = true;
|
||||
//All of this is handled directly by the region, which scan every pos from top to bottom of the quad tree
|
||||
LodRegion lodRegion = regions[x][z];
|
||||
LodRegion lodRegion = regions.get(minPos.x+x, minPos.y+z);
|
||||
|
||||
|
||||
if (lodRegion != null && lodRegion.needRecheckGenPoint) {
|
||||
@@ -724,22 +624,26 @@ public class LodDimension
|
||||
*/
|
||||
public boolean regionIsInRange(int regionX, int regionZ)
|
||||
{
|
||||
int xIndex = (regionX - center.x) + halfWidth;
|
||||
int zIndex = (regionZ - center.z) + halfWidth;
|
||||
|
||||
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
|
||||
return regions.inRange(regionX, regionZ);
|
||||
}
|
||||
|
||||
/** Returns the dimension's center region position X value */
|
||||
@Deprecated // Use getCenterRegionPos() instead
|
||||
public int getCenterRegionPosX()
|
||||
{
|
||||
return center.x;
|
||||
return regions.getCenter().x;
|
||||
}
|
||||
|
||||
/** Returns the dimension's center region position Z value */
|
||||
@Deprecated // Use getCenterRegionPos() instead
|
||||
public int getCenterRegionPosZ()
|
||||
{
|
||||
return center.z;
|
||||
return regions.getCenter().y;
|
||||
}
|
||||
|
||||
public RegionPos getCenterRegionPos() {
|
||||
Pos p = regions.getCenter();
|
||||
return new RegionPos(p.x, p.y);
|
||||
}
|
||||
|
||||
/** returns the width of the dimension in regions */
|
||||
@@ -748,7 +652,7 @@ public class LodDimension
|
||||
// we want to get the length directly from the
|
||||
// source to make sure it is in sync with region
|
||||
// and isRegionDirty
|
||||
return regions != null ? regions.length : width;
|
||||
return regions != null ? regions.getSize() : width;
|
||||
}
|
||||
|
||||
/** Update the width of this dimension, in regions */
|
||||
@@ -756,8 +660,8 @@ public class LodDimension
|
||||
{
|
||||
width = newWidth;
|
||||
halfWidth = width/ 2;
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
Pos p = regions.getCenter();
|
||||
regions = new MovabeGridRingList<LodRegion>(halfWidth, p.x, p.y);
|
||||
generateIteratorList();
|
||||
}
|
||||
|
||||
@@ -767,18 +671,7 @@ public class LodDimension
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("Dimension : \n");
|
||||
for (LodRegion[] lodRegions : regions)
|
||||
{
|
||||
for (LodRegion region : lodRegions)
|
||||
{
|
||||
if (region == null)
|
||||
stringBuilder.append("n");
|
||||
else
|
||||
stringBuilder.append(region.getMinDetailLevel());
|
||||
stringBuilder.append("\t");
|
||||
}
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
stringBuilder.append(regions.toDetailString());
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.seibel.lod.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MovabeGridRingList<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
private static final long serialVersionUID = -7743190533384530134L;
|
||||
|
||||
public static class Pos {
|
||||
public final int x;
|
||||
public final int y;
|
||||
Pos(int x, int y) {this.x=x; this.y=y;}
|
||||
}
|
||||
|
||||
private AtomicReference<Pos> pos = new AtomicReference<Pos>();
|
||||
|
||||
private final int halfSize;
|
||||
private final int size;
|
||||
private final ReentrantReadWriteLock moveLock = new ReentrantReadWriteLock();
|
||||
|
||||
public MovabeGridRingList(int halfSize, int centerX, int centerY) {
|
||||
super((halfSize * 2 + 1) * (halfSize * 2 + 1));
|
||||
size = halfSize * 2 + 1;
|
||||
this.halfSize = halfSize;
|
||||
pos.set(new Pos(centerX-halfSize, centerY-halfSize));
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
moveLock.writeLock().lock();
|
||||
try {
|
||||
super.clear();
|
||||
super.ensureCapacity(size*size);
|
||||
for (int i=0; i<size*size; i++) {
|
||||
super.add(null);
|
||||
}
|
||||
} finally {
|
||||
moveLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear(Consumer<? super T> d) {
|
||||
moveLock.writeLock().lock();
|
||||
try {
|
||||
super.forEach((t) -> {
|
||||
if (t!=null) d.accept(t);
|
||||
});
|
||||
super.clear();
|
||||
super.ensureCapacity(size*size);
|
||||
for (int i=0; i<size*size; i++) {
|
||||
super.add(null);
|
||||
}
|
||||
} finally {
|
||||
moveLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Pos getCenter() {
|
||||
Pos bottom = pos.get();
|
||||
return new Pos(bottom.x+halfSize, bottom.y+halfSize);
|
||||
}
|
||||
public Pos getMinInRange() {
|
||||
return pos.get();
|
||||
}
|
||||
public Pos getMaxInRange() {
|
||||
Pos bottom = pos.get();
|
||||
return new Pos(bottom.x+size-1, bottom.y+size-1);
|
||||
}
|
||||
public int getSize() {return size;}
|
||||
public int getHalfSize() {return halfSize;}
|
||||
|
||||
// WARNNING! Be careful with race condition!
|
||||
// The grid may get moved after this query!
|
||||
public boolean inRange(int x, int y) {
|
||||
Pos min = pos.get();
|
||||
return (x>=min.x && x<min.x+size && y>=min.y && y<min.y+size);
|
||||
}
|
||||
private boolean _inRangeAquired(int x, int y, Pos min) {
|
||||
return (x>=min.x && x<min.x+size && y>=min.y && y<min.y+size);
|
||||
}
|
||||
private T _getUnsafe(int x, int y) {
|
||||
return super.get(Math.floorMod(x, size) + Math.floorMod(y, size)*size);
|
||||
}
|
||||
private void _setUnsafe(int x, int y, T t) {
|
||||
super.set(Math.floorMod(x, size) + Math.floorMod(y, size)*size, t);
|
||||
}
|
||||
private T _swapUnsafe(int x, int y, T t) {
|
||||
return super.set(Math.floorMod(x, size) + Math.floorMod(y, size)*size, t);
|
||||
}
|
||||
|
||||
// return null if x,y is outside of the grid
|
||||
public T get(int x, int y) {
|
||||
Pos min = pos.get();
|
||||
if (!_inRangeAquired(x, y, min)) return null;
|
||||
moveLock.readLock().lock();
|
||||
try {
|
||||
Pos newMin = pos.get();
|
||||
// Use EXECT compare here
|
||||
if (min!=newMin)
|
||||
if (!_inRangeAquired(x, y, newMin)) return null;
|
||||
return _getUnsafe(x, y);
|
||||
} finally {
|
||||
moveLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// return false if x,y is outside of the grid
|
||||
public boolean set(int x, int y, T t) {
|
||||
Pos min = pos.get();
|
||||
if (!_inRangeAquired(x, y, min)) return false;
|
||||
moveLock.readLock().lock();
|
||||
try {
|
||||
Pos newMin = pos.get();
|
||||
// Use EXECT compare here
|
||||
if (min!=newMin)
|
||||
if (!_inRangeAquired(x, y, newMin)) return false;
|
||||
_setUnsafe(x, y, t);
|
||||
return true;
|
||||
} finally {
|
||||
moveLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// return input t if x,y is outside of the grid
|
||||
public T swap(int x, int y, T t) {
|
||||
Pos min = pos.get();
|
||||
if (!_inRangeAquired(x, y, min)) return t;
|
||||
moveLock.readLock().lock();
|
||||
try {
|
||||
Pos newMin = pos.get();
|
||||
// Use EXECT compare here
|
||||
if (min!=newMin)
|
||||
if (!_inRangeAquired(x, y, newMin)) return t;
|
||||
return _swapUnsafe(x, y, t);
|
||||
} finally {
|
||||
moveLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Impl this
|
||||
/*
|
||||
// do a compare and set
|
||||
public boolean compareAndSet(int x, int y, T expected, T toBeSet) {
|
||||
Pos min = pos.get();
|
||||
if (!_inRangeAquired(x, y, min)) return false;
|
||||
moveLock.readLock().lock();
|
||||
try {
|
||||
Pos newMin = pos.get();
|
||||
// Use EXECT compare here
|
||||
if (min!=newMin)
|
||||
if (!_inRangeAquired(x, y, newMin)) return false;
|
||||
return _compareAndSetUnsafe(x, y, expected, toBeSet);
|
||||
} finally {
|
||||
moveLock.readLock().unlock();
|
||||
}
|
||||
}*/
|
||||
|
||||
// return null if x,y is outside of the grid
|
||||
// Otherwise, return the new value (for chaining)
|
||||
public T setChained(int x, int y, T t) {
|
||||
return set(x,y,t) ? t : null;
|
||||
}
|
||||
|
||||
// Return false if haven't changed. Return true if it did
|
||||
public boolean move(int newCenterX, int newCenterY) {
|
||||
return move(newCenterX, newCenterY, null);
|
||||
}
|
||||
|
||||
public boolean move(int newCenterX, int newCenterY, Consumer<? super T> d) {
|
||||
Pos cPos = pos.get();
|
||||
int newMinX = newCenterX - halfSize;
|
||||
int newMinY = newCenterY - halfSize;
|
||||
if (cPos.x == newMinX && cPos.y == newMinY)
|
||||
return false;
|
||||
moveLock.writeLock().lock();
|
||||
try {
|
||||
cPos = pos.get();
|
||||
int deltaX = newMinX - cPos.x;
|
||||
int deltaY = newMinY - cPos.y;
|
||||
if (deltaX == 0 && deltaY == 0)
|
||||
return false;
|
||||
// if the x or z offset is equal to or greater than
|
||||
// the total width, just delete the current data
|
||||
// and update the pos
|
||||
if (Math.abs(deltaX) >= size || Math.abs(deltaY) >= size) {
|
||||
clear();
|
||||
} else {
|
||||
for (int x = 0; x < size; x++) {
|
||||
for (int y = 0; y < size; y++) {
|
||||
if (x - deltaX < 0 || y - deltaY < 0 || x - deltaX >= size || y - deltaY >= size) {
|
||||
T t = _swapUnsafe(x + cPos.x, y + cPos.y, null);
|
||||
if (t != null && d != null)
|
||||
d.accept(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pos.set(new Pos(newMinX, newMinY));
|
||||
return true;
|
||||
} finally {
|
||||
moveLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Pos p = pos.get();
|
||||
return "MovabeGridRingList[" + p.x+halfSize + "," + p.y+halfSize + "] " + size + "*" + size + "[" + size() + "]";
|
||||
}
|
||||
|
||||
public String toDetailString() {
|
||||
StringBuilder str = new StringBuilder("\n");
|
||||
int i = 0;
|
||||
str.append(this);
|
||||
str.append("\n");
|
||||
for (T t : this) {
|
||||
|
||||
str.append(t!=null ? t.toString() : "NULL");
|
||||
str.append(", ");
|
||||
i++;
|
||||
if (i % size == 0) {
|
||||
str.append("\n");
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
}
|
||||
@@ -14,16 +14,6 @@ public class MovableGridList<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
private static final long serialVersionUID = 5366261085254591277L;
|
||||
|
||||
public static class Pos {
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
public Pos(int xx, int yy) {
|
||||
x = xx;
|
||||
y = yy;
|
||||
}
|
||||
}
|
||||
|
||||
private int centerX;
|
||||
private int centerY;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user