3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-11-16 16:10:07 +01:00
Dieser Commit ist enthalten in:
dordsor21 2023-10-21 13:59:08 +01:00
Ursprung e3ea0c2b4a
Commit 34009d1d06
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 1E53E88969FFCF0B
8 geänderte Dateien mit 338 neuen und 636 gelöschten Zeilen

Datei anzeigen

@ -588,7 +588,7 @@ public class MCAChunk implements IChunk {
}
public boolean isModified() {
return modified;
return modified || deleted;
}
public boolean isDeleted() {

Datei anzeigen

@ -1,10 +1,11 @@
package com.fastasyncworldedit.core.anvil;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.math.FastBitSet;
import com.fastasyncworldedit.core.util.task.RunnableVal4;
import com.plotsquared.core.util.task.RunnableVal;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.MissingWorldException;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -53,26 +54,71 @@ public class MCAFile implements Closeable, Flushable {
private boolean closed = false;
private volatile boolean init = false;
public MCAFile(Path file) {
this.file = file;
if (!Files.exists(file)) {
throw new MissingWorldException();
}
String[] split = file.getFileName().toString().split("\\.");
X = Integer.parseInt(split[1]);
Z = Integer.parseInt(split[2]);
offsetMap = new Int2IntOpenHashMap();
offsetMap.defaultReturnValue(Integer.MAX_VALUE);
init();
}
public MCAFile(int mcrX, int mcrZ, Path file) {
private MCAFile(int mcrX, int mcrZ, Path file) {
this.file = file;
X = mcrX;
Z = mcrZ;
offsetMap = new Int2IntOpenHashMap();
offsetMap.defaultReturnValue(Integer.MAX_VALUE);
init();
}
/**
* Create a new mca file
*
* @param mcrX region x
* @param mcrZ region z
* @param directory directory to create mca file in
* @return new mca file
* @throws IOException on io error
*/
public static MCAFile create(int mcrX, int mcrZ, Path directory) throws IOException {
Files.createDirectories(directory);
Path file = directory.resolve("r." + mcrX + "." + mcrZ + ".mca");
Files.createFile(file);
MCAFile mca = new MCAFile(mcrX, mcrZ, file);
mca.initEmpty();
return mca;
}
/**
* Load an mca file
*
* @param mcrX region x
* @param mcrZ region z
* @param directory directory to load mca file from
* @return loaded mca file
* @throws IOException on io error
*/
@Nullable
public static MCAFile load(int mcrX, int mcrZ, Path directory) throws IOException {
Path file = directory.resolve("r." + mcrX + "." + mcrZ + ".mca");
if (!Files.exists(file)) {
return null;
}
MCAFile mca = new MCAFile(mcrX, mcrZ, file);
mca.init();
return mca;
}
/**
* Load or create an mca file
*
* @param mcrX region x
* @param mcrZ region z
* @param directory directory to load or create mca file in
* @return mca file
* @throws IOException on io error
*/
public static MCAFile loadOrCreate(int mcrX, int mcrZ, Path directory) throws IOException {
Path file = directory.resolve("r." + mcrX + "." + mcrZ + ".mca");
MCAFile mca = new MCAFile(mcrX, mcrZ, file);
if (!Files.exists(file)) {
Files.createFile(file);
mca.initEmpty();
} else {
mca.init();
}
return mca;
}
public void clear() {
@ -80,7 +126,7 @@ public class MCAFile implements Closeable, Flushable {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("Error clearing MCAFile", e);
}
}
synchronized (chunks) {
@ -94,9 +140,7 @@ public class MCAFile implements Closeable, Flushable {
* Set if the file should be delete
*/
public void setDeleted(boolean deleted) {
if (!init) {
init();
}
checkInit();
this.deleted = deleted;
}
@ -107,49 +151,68 @@ public class MCAFile implements Closeable, Flushable {
return deleted;
}
protected void checkInit() {
if (!init) {
try {
init();
} catch (IOException e) {
throw new FaweException("Error initialising MCA file", FaweException.Type.ANVIL_IO, false, e);
}
}
}
/**
* Initialises the RandomAccessFile and loads the location header from disk if not done yet
*/
public synchronized void init() {
try {
if (raf == null) {
this.offsets = new int[SECTOR_INTS];
if (file != null) {
this.raf = new RandomAccessFile(file.toFile(), "rw");
final int nSectors = (int) Math.round(Math.ceil((double) raf.length() / SECTOR_BYTES));
sectorFree = new FastBitSet(nSectors);
sectorFree.setAll();
sectorFree.set(0, false);
sectorFree.set(1, false);
if (raf.length() < 8192) {
raf.setLength(8192);
} else {
if ((raf.length() & 0xFFF) != 0) {
raf.setLength(((raf.length() + 0xFFF) >> 12) << 12);
}
raf.seek(0);
for (int i = 0; i < SECTOR_INTS; i++) {
final int offset = raf.readInt();
offsets[i] = offset;
int sectorStart = offset >> 8;
int numSectors = offset & 0xFF;
if (offset != 0 && sectorStart + numSectors <= sectorFree.size()) {
offsetMap.put(offset, i);
for (int sectorNum = 0; sectorNum < (offset & 0xFF); sectorNum++) {
sectorFree.set((offset >> 8) + sectorNum, false);
}
private synchronized void init() throws IOException {
if (raf == null) {
this.offsets = new int[SECTOR_INTS];
if (file != null) {
this.raf = new RandomAccessFile(file.toFile(), "rw");
final int nSectors = (int) Math.round(Math.ceil((double) raf.length() / SECTOR_BYTES));
sectorFree = new FastBitSet(nSectors);
sectorFree.setAll();
sectorFree.set(0, false);
sectorFree.set(1, false);
if (raf.length() < 8192) {
raf.setLength(8192);
} else {
if ((raf.length() & 0xFFF) != 0) {
raf.setLength(((raf.length() + 0xFFF) >> 12) << 12);
}
raf.seek(0);
for (int i = 0; i < SECTOR_INTS; i++) {
final int offset = raf.readInt();
offsets[i] = offset;
int sectorStart = offset >> 8;
int numSectors = offset & 0xFF;
if (offset != 0 && sectorStart + numSectors <= sectorFree.size()) {
offsetMap.put(offset, i);
for (int sectorNum = 0; sectorNum < (offset & 0xFF); sectorNum++) {
sectorFree.set((offset >> 8) + sectorNum, false);
}
}
}
}
init = true;
closed = false;
}
} catch (Throwable e) {
e.printStackTrace();
init = true;
closed = false;
}
}
private synchronized void initEmpty() throws IOException {
this.offsets = new int[SECTOR_INTS];
this.raf = new RandomAccessFile(file.toFile(), "rw");
// Initialises with all zeroes
this.raf.setLength(8192L);
this.raf.seek(0);
this.raf.write(FaweCache.INSTANCE.BYTE_BUFFER_8192.get());
sectorFree = new FastBitSet(2);
init = true;
closed = false;
}
/**
* Get the region file X
*/
@ -183,9 +246,7 @@ public class MCAFile implements Closeable, Flushable {
*/
@Nullable
public MCAChunk getCachedChunk(int cx, int cz) {
if (!init) {
init();
}
checkInit();
short pair = (short) ((cx & 31) + ((cz & 31) << 5));
synchronized (chunks) {
return chunks.get(pair);
@ -196,9 +257,7 @@ public class MCAFile implements Closeable, Flushable {
* Create a new empty {@link MCAChunk}.
*/
public MCAChunk newChunk(int cx, int cz) {
if (!init) {
init();
}
checkInit();
short pair = (short) ((cx & 31) + ((cz & 31) << 5));
MCAChunk chunk;
synchronized (chunks) {
@ -211,9 +270,7 @@ public class MCAFile implements Closeable, Flushable {
* Insert a {@link MCAChunk} into the cache.
*/
public void setChunk(MCAChunk chunk) {
if (!init) {
init();
}
checkInit();
int cx = chunk.getX();
int cz = chunk.getZ();
short pair = (short) ((cx & 31) + ((cz & 31) << 5));
@ -226,9 +283,7 @@ public class MCAFile implements Closeable, Flushable {
* Load data from the mca region into the given {@link MCAChunk}.
*/
public void loadIntoChunkFromFile(MCAChunk chunk) throws IOException {
if (!init) {
init();
}
checkInit();
int cx = chunk.getX();
int cz = chunk.getZ();
int i = (cx & 31) + ((cz & 31) << 5);
@ -238,37 +293,13 @@ public class MCAFile implements Closeable, Flushable {
chunk.setEmpty(true);
return;
}
chunk.loadFromNIS(getChunkIS(offset >> 8), false);
if (offset == 0) {
return;
}
if (i < 2) {
int length;
byte version;
byte[] data;
synchronized (this) {
raf.seek((long) (offset >> 8) << 12);
length = raf.readInt();
version = raf.readByte();
data = new byte[length - 1];
raf.read(data);
}
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
BufferedInputStream bis = switch (version) {
case VERSION_GZIP -> new BufferedInputStream(new GZIPInputStream(bais));
case VERSION_DEFLATE -> new BufferedInputStream(new InflaterInputStream(bais));
case VERSION_UNCOMPRESSED -> new BufferedInputStream(bais);
default -> throw new IllegalStateException("Unexpected compression version: " + version);
};
}
chunk.loadFromNIS(new NBTInputStream(getChunkBIS(offset >> 8)), false);
}
}
@Nonnull
public MCAChunk getChunk(int cx, int cz) throws IOException {
if (!init) {
init();
}
public MCAChunk getChunk(int cx, int cz) {
checkInit();
MCAChunk cached = getCachedChunk(cx, cz);
if (cached != null) {
return cached;
@ -277,10 +308,8 @@ public class MCAFile implements Closeable, Flushable {
}
}
public MCAChunk readChunk(int cx, int cz) throws IOException {
if (!init) {
init();
}
public MCAChunk readChunk(int cx, int cz) {
checkInit();
int i = (cx & 31) + ((cz & 31) << 5);
int offset = offsets[i];
if (offset == 0) {
@ -289,26 +318,8 @@ public class MCAFile implements Closeable, Flushable {
try {
MCAChunk chunk;
synchronized (this) {
chunk = getChunkIS(offset >> 8, cx, cz);
}
if (i < 2) {
int length;
byte version;
byte[] data;
synchronized (this) {
raf.seek((long) (offset >> 8) << 12);
length = raf.readInt();
version = raf.readByte();
data = new byte[length - 1];
raf.read(data);
}
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
BufferedInputStream bis = switch (version) {
case VERSION_GZIP -> new BufferedInputStream(new GZIPInputStream(bais));
case VERSION_DEFLATE -> new BufferedInputStream(new InflaterInputStream(bais));
case VERSION_UNCOMPRESSED -> new BufferedInputStream(bais);
default -> throw new IllegalStateException("Unexpected compression version: " + version);
};
NBTInputStream nis = new NBTInputStream(getChunkBIS(offset >> 8));
chunk = new MCAChunk(this, nis, cx, cz, false);
}
short pair = (short) ((cx & 31) + ((cz & 31) << 5));
synchronized (chunks) {
@ -322,12 +333,10 @@ public class MCAFile implements Closeable, Flushable {
}
/**
* @param onEach cx, cz, offset, size (in kB)
* @param onEach cx, cz, sector, sectorsAllocated (in kB)
*/
public void forEachChunk(RunnableVal4<Integer, Integer, Integer, Integer> onEach) {
if (!init) {
init();
}
checkInit();
int i = 0;
for (int z = 0; z < 32; z++) {
for (int x = 0; x < 32; x++, i += 4) {
@ -341,9 +350,7 @@ public class MCAFile implements Closeable, Flushable {
}
public void forEachChunk(RunnableVal<MCAChunk> onEach) {
if (!init) {
init();
}
checkInit();
int rx = X << 5;
int rz = Z << 5;
for (int z = 0; z < 32; z++) {
@ -359,60 +366,90 @@ public class MCAFile implements Closeable, Flushable {
}
}
private NBTInputStream getChunkIS(int offset) throws IOException {
int length = -1;
byte version = -1;
byte[] data;
try {
if (offset == 0) {
return null;
}
synchronized (this) {
raf.seek((long) offset << 12);
length = raf.readInt();
version = raf.readByte();
data = new byte[length - 1];
raf.read(data);
}
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
BufferedInputStream bis = switch (version) {
case VERSION_GZIP -> new BufferedInputStream(new GZIPInputStream(bais));
case VERSION_DEFLATE -> new BufferedInputStream(new InflaterInputStream(bais));
case VERSION_UNCOMPRESSED -> new BufferedInputStream(bais);
default -> throw new IllegalStateException("Unexpected compression version: " + version);
};
return new NBTInputStream(bis);
} catch (IOException e) {
throw new IOException("Length: " + length + ", version: " + version + ", offset: " + offset, e);
}
@Nullable
protected BufferedInputStream getChunkIS(int cx, int cz) throws IOException {
checkInit();
int offset = offsets[(cx & 31) + ((cz & 31) << 5)];
return getChunkBIS(offset);
}
private MCAChunk getChunkIS(int offset, int cx, int cz) throws IOException {
int length = -1;
byte version = -1;
byte[] data;
try {
if (offset == 0) {
return null;
}
synchronized (this) {
raf.seek((long) offset << 12);
length = raf.readInt();
version = raf.readByte();
data = new byte[length - 1];
raf.read(data);
}
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
BufferedInputStream bis = switch (version) {
case VERSION_GZIP -> new BufferedInputStream(new GZIPInputStream(bais));
case VERSION_DEFLATE -> new BufferedInputStream(new InflaterInputStream(bais));
case VERSION_UNCOMPRESSED -> new BufferedInputStream(bais);
default -> throw new IllegalStateException("Unexpected compression version: " + version);
};
return new MCAChunk(this, new NBTInputStream(bis), cx, cz, false);
} catch (Exception e) {
throw new IOException("Length: " + length + ", version: " + version + ", offset: " + offset, e);
@Nullable
private BufferedInputStream getChunkBIS(long offset) throws IOException {
if (offset == 0) {
return null;
}
byte version;
byte[] data;
synchronized (this) {
raf.seek(offset << 12);
int length = raf.readInt();
version = raf.readByte();
data = new byte[length - 1];
raf.read(data);
}
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
return switch (version) {
case VERSION_GZIP -> new BufferedInputStream(new GZIPInputStream(bais));
case VERSION_DEFLATE -> new BufferedInputStream(new InflaterInputStream(bais));
case VERSION_UNCOMPRESSED -> new BufferedInputStream(bais);
default -> throw new IllegalStateException("Unexpected compression version: " + version);
};
}
public byte[] getFullChunkBytes(int cx, int cz) throws IOException {
checkInit();
int offset = offsets[(cx & 31) + ((cz & 31) << 5)];
int sectorNumber = offset >> 8;
int sectorsAllocated = offset & 0xFF;
byte[] data = new byte[sectorsAllocated << 12];
raf.seek((long) sectorNumber << 12);
raf.read(data);
return data;
}
/**
* Writes chunk bytes directly to the MCAFile and returns the next sector after the chunk
*
* @param cx chunk x
* @param cz chunk z
* @param sectorNumber sector to write chunk from
* @param bytes byte to write
* @return next sector after the chunk
* @throws IOException on IO error
*/
public synchronized int writeFullChunkBytes(int cx, int cz, int sectorNumber, byte[] bytes) throws IOException {
checkInit();
int allocate = (bytes.length >> 12) + 1;
int offset = (sectorNumber << 8) | (allocate & 0xFF);
setOffset(cx, cz, offset);
raf.seek((long) sectorNumber << 12);
raf.write(bytes);
int newSector = sectorNumber + allocate;
sectorFree.expandTo(newSector, false);
return newSector;
}
public synchronized void replaceChunkBytes(int cx, int cz, byte[] bytes) throws IOException {
checkInit();
int currOffset = offsets[(cx & 31) + ((cz & 31) << 5)];
int allocate = (bytes.length >> 12) + 1;
int sectorNumber = sectorFree.size() + 1;
if (currOffset != 0) {
int currSector = currOffset >> 8;
int currAllocate = currOffset & 0xFF;
sectorFree.setRange(currSector, currSector + currAllocate);
if (allocate <= currAllocate) {
sectorNumber = currSector;
sectorFree.clearRange(currSector, currSector + allocate);
}
System.out.println(sectorNumber);
}
int offset = (sectorNumber << 8) | (allocate & 0xFF);
setOffset(cx, cz, offset);
raf.seek((long) sectorNumber << 12);
raf.write(bytes);
int newSector = sectorNumber + allocate;
sectorFree.expandTo(newSector, false);
}
public List<MCAChunk> getCachedChunks() {
@ -428,16 +465,17 @@ public class MCAFile implements Closeable, Flushable {
}
}
@Override
public synchronized void close() throws IOException {
public synchronized void close(boolean flush) throws IOException {
if (raf == null || closed) {
return;
}
flush();
if (flush) {
flush();
}
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("Error closing MCAFile", e);
}
raf = null;
offsets = null;
@ -446,6 +484,11 @@ public class MCAFile implements Closeable, Flushable {
init = false;
}
@Override
public synchronized void close() throws IOException {
close(true);
}
public boolean isModified() {
if (isDeleted()) {
return true;
@ -453,7 +496,7 @@ public class MCAFile implements Closeable, Flushable {
synchronized (chunks) {
for (Int2ObjectMap.Entry<MCAChunk> entry : chunks.int2ObjectEntrySet()) {
MCAChunk chunk = entry.getValue();
if (chunk.isModified() || chunk.isDeleted()) {
if (chunk.isModified()) {
return true;
}
}
@ -461,15 +504,19 @@ public class MCAFile implements Closeable, Flushable {
return false;
}
public synchronized void setOffset(final int x, final int z, final int offset)
public int getOffset(int cx, int cz) {
return offsets[cx + (cz << 5)];
}
public synchronized void setOffset(int cx, int cz, final int offset)
throws IOException {
int i = (x & 31) + ((z & 31) << 5);
int i = (cx & 31) + ((cz & 31) << 5);
if (offset == 0) {
offsetMap.remove(offsets[i]);
} else {
offsetMap.put(offset, i);
}
offsets[x + (z << 5)] = offset;
offsets[cx + (cz << 5)] = offset;
raf.seek((long) i << 2);
raf.writeInt(offset);
}
@ -479,6 +526,7 @@ public class MCAFile implements Closeable, Flushable {
*/
@Override
public synchronized void flush() throws IOException {
System.out.println(sectorFree);
boolean delete = true;
int currentSector = 2;
Queue<Integer> offsets =
@ -577,11 +625,12 @@ public class MCAFile implements Closeable, Flushable {
int size = 0;
for (int i = sectorFree.size(); i > 0; i--) {
if (!sectorFree.get(i)) {
size = i + 1;
size = i + 1; // + 1 to fully encompass
break;
}
}
raf.setLength((long) (size + 1) * SECTOR_BYTES);
System.out.println(sectorFree);
raf.setLength((long) size << 12);
if (delete || size < 3) {
clear();
Files.delete(file);

Datei anzeigen

@ -1,6 +1,7 @@
package com.fastasyncworldedit.core.anvil;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.MathMan;
@ -64,7 +65,9 @@ public class MCAWorld extends AbstractWorld {
* @param folder World file/folder
*/
public static synchronized MCAWorld of(String name, Path folder) {
if (Fawe.platform().isWorldLoaded(name)) {
if (folder.getParent().toAbsolutePath().equals(Fawe.platform().getWorldsFolder().toAbsolutePath()) && Fawe
.platform()
.isWorldLoaded(name)) {
throw new IllegalStateException("World " + name + " is loaded. Anvil operations cannot be completed on a loaded world.");
}
// World could be the same name but in a different folder
@ -78,10 +81,18 @@ public class MCAWorld extends AbstractWorld {
short regionX = Short.parseShort(split[1]);
short regionZ = Short.parseShort(split[2]);
int paired = MathMan.pair(regionX, regionZ);
mcaFileCache.computeIfAbsent(
paired,
(i) -> new MCAFile(regionX, regionZ, regionFolder.resolve("r." + regionX + "." + regionZ + ".mca"))
);
mcaFileCache.computeIfAbsent(paired, (i) -> {
try {
return MCAFile.load(regionX, regionZ, regionFolder);
} catch (IOException e) {
throw new FaweException(
"Error loading MCA file: `" + file.getFileName() + "`",
FaweException.Type.ANVIL_IO,
false,
e
);
}
});
});
return mcaFileCache.values();
}
@ -95,6 +106,29 @@ public class MCAWorld extends AbstractWorld {
}
}
/**
* Get or create MCA file for the given region coordinates
*
* @param mcrX x region coordinate
* @param mcrZ z region coordinate
* @return mca file
*/
public MCAFile getOrCreateMCA(int mcrX, int mcrZ) {
int pair = MathMan.pair((short) mcrX, (short) mcrZ);
return mcaFileCache.computeIfAbsent(pair, (i) -> {
try {
return MCAFile.loadOrCreate(mcrX, mcrZ, regionFolder);
} catch (IOException e) {
throw new FaweException(
"Error loading MCA file " + mcrX + "," + mcrZ + " in folder " + regionFolder,
FaweException.Type.ANVIL_IO,
false,
e
);
}
});
}
@Override
public boolean setTile(final int x, final int y, final int z, final CompoundTag tile) throws WorldEditException {
return false;
@ -114,6 +148,10 @@ public class MCAWorld extends AbstractWorld {
return folder;
}
public Path getRegionFolder() {
return regionFolder;
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(
final BlockVector3 position,
@ -173,14 +211,20 @@ public class MCAWorld extends AbstractWorld {
int paired = MathMan.pair(regionX, regionZ);
MCAFile mca = mcaFileCache.computeIfAbsent(
paired,
(i) -> new MCAFile(regionX, regionZ, regionFolder.resolve("r." + regionX + "." + regionZ + ".mca"))
(i) -> {
try {
return MCAFile.loadOrCreate(regionX, regionZ, regionFolder);
} catch (IOException e) {
throw new FaweException(
"Error loading MCA file " + regionX + "," + regionZ + " in folder " + regionFolder,
FaweException.Type.ANVIL_IO,
false,
e
);
}
}
);
try {
return mca.getChunk(chunkX, chunkZ);
} catch (IOException e) {
LOGGER.error("Error loading chunk. Creating empty chunk.", e);
return mca.newChunk(chunkX, chunkZ);
}
return mca.getChunk(chunkX, chunkZ);
}
@Override
@ -192,11 +236,31 @@ public class MCAWorld extends AbstractWorld {
public synchronized void flush() {
for (MCAFile mca : mcaFileCache.values()) {
try {
mca.close();
mca.flush();
} catch (IOException e) {
LOGGER.error("Could not flush MCAFile {}", mca.getFile().getFileName(), e);
LOGGER.error("Could not close MCAFile {}", mca.getFile().getFileName(), e);
}
}
}
public synchronized void close(boolean flush) {
for (MCAFile mca : mcaFileCache.values()) {
try {
mca.close(flush);
} catch (IOException e) {
LOGGER.error("Could not close MCAFile {}", mca.getFile().getFileName(), e);
}
}
}
/**
* Create a new MCA file for the given region x,z
*
* @param rX region X
* @param rZ region Z
*/
public synchronized MCAFile createMCA(int rX, int rZ) throws IOException {
return MCAFile.create(rX, rZ, regionFolder);
}
}

Datei anzeigen

@ -97,7 +97,7 @@ public final class LimitExtent extends AbstractDelegateExtent implements IBatchP
}
private void handleException(FaweException e) {
if (e.ignorable() || !limit.MAX_FAILS()) {
if (!e.ignorable() || !limit.MAX_FAILS()) {
throw e;
}
if (!faweExceptionReasonsUsed[e.getType().ordinal()]) {

Datei anzeigen

@ -23,6 +23,26 @@ public class FaweException extends RuntimeException {
this(TextComponent.of(reason));
}
/**
* New instance. Defaults to {@link FaweException.Type#OTHER}.
*/
public FaweException(Component reason, Type type, boolean ignorable, Exception e) {
super(e);
this.message = reason;
this.type = type;
this.ignorable = ignorable;
}
/**
* New instance. Defaults to {@link FaweException.Type#OTHER}.
*/
public FaweException(String reason, Type type, boolean ignorable, Exception e) {
super(e);
this.message = TextComponent.of(reason);
this.type = type;
this.ignorable = ignorable;
}
/**
* New instance. Defaults to {@link FaweException.Type#OTHER}.
*/
@ -114,6 +134,7 @@ public class FaweException extends RuntimeException {
ACTOR_REQUIRED,
CLIPBOARD,
HISTORY,
ANVIL_IO,
OTHER
}

Datei anzeigen

@ -1,434 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fastasyncworldedit.core.internal.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
/**
* A <code>BufferedRandomAccessFile</code> is like a
* <code>RandomAccessFile</code>, but it uses a private buffer so that most
* operations do not require a disk access.
* <P>
*
* Note: The operations on this class are unmonitored. Also, the correct
* functioning of the <code>RandomAccessFile</code> methods that are not
* overridden here relies on the implementation of those methods in the
* superclass.
* Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com )
*/
public class BufferedRandomAccessFile extends RandomAccessFile {
static final int LogBuffSz_ = 16; // 64K buffer
public static final int BuffSz_ = (1 << LogBuffSz_);
static final long BuffMask_ = ~(((long) BuffSz_) - 1L);
/*
* This implementation is based on the buffer implementation in Modula-3's
* "Rd", "Wr", "RdClass", and "WrClass" interfaces.
*/
private boolean dirty_; // true iff unflushed bytes exist
private boolean closed_; // true iff the file is closed
private long curr_; // current position in file
private long lo_, hi_; // bounds on characters in "buff"
private byte[] buff_; // local buffer
private long maxHi_; // this.lo + this.buff.length
private boolean hitEOF_; // buffer contains last file block?
private long diskPos_; // disk position
/*
* To describe the above fields, we introduce the following abstractions for
* the file "f":
*
* len(f) the length of the file curr(f) the current position in the file
* c(f) the abstract contents of the file disk(f) the contents of f's
* backing disk file closed(f) true iff the file is closed
*
* "curr(f)" is an index in the closed interval [0, len(f)]. "c(f)" is a
* character sequence of length "len(f)". "c(f)" and "disk(f)" may differ if
* "c(f)" contains unflushed writes not reflected in "disk(f)". The flush
* operation has the effect of making "disk(f)" identical to "c(f)".
*
* A file is said to be *valid* if the following conditions hold:
*
* V1. The "closed" and "curr" fields are correct:
*
* f.closed == closed(f) f.curr == curr(f)
*
* V2. The current position is either contained in the buffer, or just past
* the buffer:
*
* f.lo <= f.curr <= f.hi
*
* V3. Any (possibly) unflushed characters are stored in "f.buff":
*
* (forall i in [f.lo, f.curr): c(f)[i] == f.buff[i - f.lo])
*
* V4. For all characters not covered by V3, c(f) and disk(f) agree:
*
* (forall i in [f.lo, len(f)): i not in [f.lo, f.curr) => c(f)[i] ==
* disk(f)[i])
*
* V5. "f.dirty" is true iff the buffer contains bytes that should be
* flushed to the file; by V3 and V4, only part of the buffer can be dirty.
*
* f.dirty == (exists i in [f.lo, f.curr): c(f)[i] != f.buff[i - f.lo])
*
* V6. this.maxHi == this.lo + this.buff.length
*
* Note that "f.buff" can be "null" in a valid file, since the range of
* characters in V3 is empty when "f.lo == f.curr".
*
* A file is said to be *ready* if the buffer contains the current position,
* i.e., when:
*
* R1. !f.closed && f.buff != null && f.lo <= f.curr && f.curr < f.hi
*
* When a file is ready, reading or writing a single byte can be performed
* by reading or writing the in-memory buffer without performing a disk
* operation.
*/
/**
* Open a new <code>BufferedRandomAccessFile</code> on <code>file</code>
* in mode <code>mode</code>, which should be "r" for reading only, or
* "rw" for reading and writing.
*/
public BufferedRandomAccessFile(File file, String mode) throws IOException {
super(file, mode);
this.init(0);
}
public BufferedRandomAccessFile(File file, String mode, int size) throws IOException {
super(file, mode);
this.init(size);
}
/**
* Open a new <code>BufferedRandomAccessFile</code> on the file named
* <code>name</code> in mode <code>mode</code>, which should be "r" for
* reading only, or "rw" for reading and writing.
*/
public BufferedRandomAccessFile(String name, String mode) throws IOException {
super(name, mode);
this.init(0);
}
public BufferedRandomAccessFile(String name, String mode, int size) throws FileNotFoundException {
super(name, mode);
this.init(size);
}
public BufferedRandomAccessFile(File file, String mode, byte[] buf) throws FileNotFoundException {
super(file, mode);
this.dirty_ = this.closed_ = false;
this.lo_ = this.curr_ = this.hi_ = 0;
this.buff_ = buf;
this.maxHi_ = (long) BuffSz_;
this.hitEOF_ = false;
this.diskPos_ = 0L;
}
private void init(int size) {
this.dirty_ = this.closed_ = false;
this.lo_ = this.curr_ = this.hi_ = 0;
this.buff_ = (size > BuffSz_) ? new byte[size] : new byte[BuffSz_];
this.maxHi_ = (long) BuffSz_;
this.hitEOF_ = false;
this.diskPos_ = 0L;
}
@Override
public void close() throws IOException {
this.flush();
this.closed_ = true;
super.close();
}
/**
* Flush any bytes in the file's buffer that have not yet been written to
* disk. If the file was created read-only, this method is a no-op.
*/
public void flush() throws IOException {
this.flushBuffer();
}
/* Flush any dirty bytes in the buffer to disk. */
private void flushBuffer() throws IOException {
if (this.dirty_) {
if (this.diskPos_ != this.lo_)
super.seek(this.lo_);
int len = (int) (this.curr_ - this.lo_);
super.write(this.buff_, 0, len);
this.diskPos_ = this.curr_;
this.dirty_ = false;
}
}
/*
* Read at most "this.buff.length" bytes into "this.buff", returning the
* number of bytes read. If the return result is less than
* "this.buff.length", then EOF was read.
*/
private int fillBuffer() throws IOException {
int cnt = 0;
int rem = this.buff_.length;
while (rem > 0) {
int n = super.read(this.buff_, cnt, rem);
if (n < 0)
break;
cnt += n;
rem -= n;
}
if ((cnt < 0) && (this.hitEOF_ = (cnt < this.buff_.length))) {
// make sure buffer that wasn't read is initialized with -1
Arrays.fill(this.buff_, cnt, this.buff_.length, (byte) 0xff);
}
this.diskPos_ += cnt;
return cnt;
}
/*
* This method positions <code>this.curr</code> at position <code>pos</code>.
* If <code>pos</code> does not fall in the current buffer, it flushes the
* current buffer and loads the correct one.<p>
*
* On exit from this routine <code>this.curr == this.hi</code> iff <code>pos</code>
* is at or past the end-of-file, which can only happen if the file was
* opened in read-only mode.
*/
@Override
public void seek(long pos) throws IOException {
if (pos >= this.hi_ || pos < this.lo_) {
// seeking outside of current buffer -- flush and read
this.flushBuffer();
this.lo_ = pos & BuffMask_; // start at BuffSz boundary
this.maxHi_ = this.lo_ + (long) this.buff_.length;
if (this.diskPos_ != this.lo_) {
super.seek(this.lo_);
this.diskPos_ = this.lo_;
}
int n = this.fillBuffer();
this.hi_ = this.lo_ + (long) n;
} else {
// seeking inside current buffer -- no read required
if (pos < this.curr_) {
// if seeking backwards, we must flush to maintain V4
this.flushBuffer();
}
}
this.curr_ = pos;
}
/*
* Does not maintain V4 (i.e. buffer differs from disk contents if previously written to)
* - Assumes no writes were made
* @param pos
* @throws IOException
*/
public void seekUnsafe(long pos) throws IOException {
if (pos >= this.hi_ || pos < this.lo_) {
// seeking outside of current buffer -- flush and read
this.flushBuffer();
this.lo_ = pos & BuffMask_; // start at BuffSz boundary
this.maxHi_ = this.lo_ + (long) this.buff_.length;
if (this.diskPos_ != this.lo_) {
super.seek(this.lo_);
this.diskPos_ = this.lo_;
}
int n = this.fillBuffer();
this.hi_ = this.lo_ + (long) n;
}
this.curr_ = pos;
}
@Override
public long getFilePointer() {
return this.curr_;
}
@Override
public long length() throws IOException {
return Math.max(this.curr_, super.length());
}
@Override
public int read() throws IOException {
if (this.curr_ >= this.hi_) {
// test for EOF
// if (this.hi < this.maxHi) return -1;
if (this.hitEOF_)
return -1;
// slow path -- read another buffer
this.seek(this.curr_);
if (this.curr_ == this.hi_)
return -1;
}
byte res = this.buff_[(int) (this.curr_ - this.lo_)];
this.curr_++;
return ((int) res) & 0xFF; // convert byte -> int
}
public byte read1() throws IOException {
if (this.curr_ >= this.hi_) {
// test for EOF
// if (this.hi < this.maxHi) return -1;
if (this.hitEOF_)
return -1;
// slow path -- read another buffer
this.seek(this.curr_);
if (this.curr_ == this.hi_)
return -1;
}
byte res = this.buff_[(int) (this.curr_ - this.lo_)];
this.curr_++;
return res;
}
@Override
public int read(byte[] b) throws IOException {
return this.read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (this.curr_ >= this.hi_) {
// test for EOF
// if (this.hi < this.maxHi) return -1;
if (this.hitEOF_)
return -1;
// slow path -- read another buffer
this.seek(this.curr_);
if (this.curr_ == this.hi_)
return -1;
}
len = Math.min(len, (int) (this.hi_ - this.curr_));
int buffOff = (int) (this.curr_ - this.lo_);
System.arraycopy(this.buff_, buffOff, b, off, len);
this.curr_ += len;
return len;
}
public byte readCurrent() throws IOException {
if (this.curr_ >= this.hi_) {
// test for EOF
// if (this.hi < this.maxHi) return -1;
if (this.hitEOF_)
return -1;
// slow path -- read another buffer
this.seek(this.curr_);
if (this.curr_ == this.hi_)
return -1;
}
byte res = this.buff_[(int) (this.curr_ - this.lo_)];
return res;
}
public void writeCurrent(byte b) throws IOException {
if (this.curr_ >= this.hi_) {
if (this.hitEOF_ && this.hi_ < this.maxHi_) {
// at EOF -- bump "hi"
this.hi_++;
} else {
// slow path -- write current buffer; read next one
this.seek(this.curr_);
if (this.curr_ == this.hi_) {
// appending to EOF -- bump "hi"
this.hi_++;
}
}
}
this.buff_[(int) (this.curr_ - this.lo_)] = (byte) b;
this.dirty_ = true;
}
public void writeUnsafe(int b) throws IOException {
this.buff_[(int) (this.curr_ - this.lo_)] = (byte) b;
this.curr_++;
this.dirty_ = true;
}
@Override
public void write(int b) throws IOException {
if (this.curr_ >= this.hi_) {
if (this.hitEOF_ && this.hi_ < this.maxHi_) {
// at EOF -- bump "hi"
this.hi_++;
} else {
// slow path -- write current buffer; read next one
this.seek(this.curr_);
if (this.curr_ == this.hi_) {
// appending to EOF -- bump "hi"
this.hi_++;
}
}
}
this.buff_[(int) (this.curr_ - this.lo_)] = (byte) b;
this.curr_++;
this.dirty_ = true;
}
@Override
public void write(byte[] b) throws IOException {
this.write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
while (len > 0) {
int n = this.writeAtMost(b, off, len);
off += n;
len -= n;
this.dirty_ = true;
}
}
/*
* Write at most "len" bytes to "b" starting at position "off", and return
* the number of bytes written.
*/
private int writeAtMost(byte[] b, int off, int len) throws IOException {
if (this.curr_ >= this.hi_) {
if (this.hitEOF_ && this.hi_ < this.maxHi_) {
// at EOF -- bump "hi"
this.hi_ = this.maxHi_;
} else {
// slow path -- write current buffer; read next one
this.seek(this.curr_);
if (this.curr_ == this.hi_) {
// appending to EOF -- bump "hi"
this.hi_ = this.maxHi_;
}
}
}
len = Math.min(len, (int) (this.hi_ - this.curr_));
int buffOff = (int) (this.curr_ - this.lo_);
System.arraycopy(b, off, this.buff_, buffOff, len);
this.curr_ += len;
return len;
}
}

Datei anzeigen

@ -95,10 +95,9 @@ public class FastBitSet {
}
public void expandTo(int newSize, boolean value) {
//System.out.println(newSize);
int newLength = (newSize + 64) >> 6;
if (newLength <= this.bits.length) {
if (this.size > newSize) {
if (this.size < newSize) {
this.size = newSize;
}
return;

Datei anzeigen

@ -31,8 +31,11 @@
"fawe.worldedit.paste.command.paste": "The clipboard has been pasted at {0}",
"fawe.worldedit.history.command.undo.disabled": "Undo disabled, use: //fast",
"fawe.worldedit.selection.selection.count": "Counted {0} blocks.",
"fawe.worldedit.anvil.world.is.loaded": "The world shouldn't be in use when executing. Unload the world, or use -f to override (save first)",
"fawe.worldedit.anvil.error.mca.init.failed": "Failed to initialise MCA file.",
"fawe.worldedit.anvil.error.world.loaded": "The world shouldn't be in use when executing. Unload the world, or use -f to override (save first)",
"fawe.worldedit.anvil.replaceall.complete": "Replaceall with anvil completed.",
"fawe.worldedit.anvil.replace.complete": "Replace with anvil completed.",
"fawe.worldedit.anvil.set.complete": "Set with anvil completed.",
"fawe.worldedit.brush.brush.reset": "Reset your brush. (SHIFT + Click)",
"fawe.worldedit.brush.brush.none": "You aren't holding a brush!",
"fawe.worldedit.brush.brush.scroll.action.set": "Set scroll action to {0}",