feat: improvements to clipboard on disk (#2162)

* feat: improvements to clipboard on disk
 - close clipboard on instantiation failure
 - set canHaveBiomes when loading clipboard from disk (only possible cause I can find for #2151 ..?)
 - check file length and give more appropriate error if it exceeds maximum. Fixes #2151 I guess?#
 - set byteBuffer to null before invoking cleaning. Addresses #1985
 - don't catch and print all exceptions when getBlock fails - prevents large console output for what is likely to be a failed operation anyway

* Fix comments
This commit is contained in:
Jordan 2023-03-29 23:13:50 +01:00 committed by GitHub
parent d82bf0527e
commit 508b94ddc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -64,9 +64,9 @@ public class DiskOptimizedClipboard extends LinearClipboard {
private final int headerSize; private final int headerSize;
private RandomAccessFile braf; private RandomAccessFile braf;
private MappedByteBuffer byteBuffer; private MappedByteBuffer byteBuffer = null;
private FileChannel fileChannel; private FileChannel fileChannel = null;
private boolean hasBiomes = false; private boolean hasBiomes = false;
private boolean canHaveBiomes = true; private boolean canHaveBiomes = true;
private int nbtBytesRemaining; private int nbtBytesRemaining;
@ -133,7 +133,7 @@ public class DiskOptimizedClipboard extends LinearClipboard {
e.printStackTrace(); e.printStackTrace();
} }
this.braf = new RandomAccessFile(file, "rw"); this.braf = new RandomAccessFile(file, "rw");
long fileLength = (long) getVolume() * 2L + (long) headerSize; long fileLength = (long) (getVolume() << 1) + (long) headerSize;
braf.setLength(0); braf.setLength(0);
braf.setLength(fileLength); braf.setLength(fileLength);
this.nbtBytesRemaining = Integer.MAX_VALUE - (int) fileLength; this.nbtBytesRemaining = Integer.MAX_VALUE - (int) fileLength;
@ -144,7 +144,11 @@ public class DiskOptimizedClipboard extends LinearClipboard {
byteBuffer.putChar(6, (char) getHeight()); byteBuffer.putChar(6, (char) getHeight());
byteBuffer.putChar(8, (char) getLength()); byteBuffer.putChar(8, (char) getLength());
} catch (IOException e) { } catch (IOException e) {
close();
throw new RuntimeException(e); throw new RuntimeException(e);
} catch (Throwable t) {
close();
throw t;
} }
} }
@ -169,11 +173,15 @@ public class DiskOptimizedClipboard extends LinearClipboard {
nbtMap = new HashMap<>(); nbtMap = new HashMap<>();
try { try {
this.file = file; this.file = file;
checkFileLength(file);
this.braf = new RandomAccessFile(file, "rw"); this.braf = new RandomAccessFile(file, "rw");
braf.setLength(file.length()); braf.setLength(file.length());
this.nbtBytesRemaining = Integer.MAX_VALUE - (int) file.length(); this.nbtBytesRemaining = Integer.MAX_VALUE - (int) file.length();
init(); init();
long biomeLength = (long) ((getHeight() >> 2) + 1) * ((getLength() >> 2) + 1) * ((getWidth() >> 2) + 1);
int biomeLength = ((getHeight() >> 2) + 1) * ((getLength() >> 2) + 1) * ((getWidth() >> 2) + 1);
canHaveBiomes = (long) headerSize + biomeLength < Integer.MAX_VALUE;
if (headerSize >= VERSION_2_HEADER_SIZE) { if (headerSize >= VERSION_2_HEADER_SIZE) {
readBiomeStatusFromHeader(); readBiomeStatusFromHeader();
int nbtCount = readNBTSavedCountFromHeader(); int nbtCount = readNBTSavedCountFromHeader();
@ -181,12 +189,42 @@ public class DiskOptimizedClipboard extends LinearClipboard {
if (Settings.settings().CLIPBOARD.SAVE_CLIPBOARD_NBT_TO_DISK && (nbtCount + entitiesCount > 0)) { if (Settings.settings().CLIPBOARD.SAVE_CLIPBOARD_NBT_TO_DISK && (nbtCount + entitiesCount > 0)) {
loadNBTFromFileFooter(nbtCount, entitiesCount, biomeLength); loadNBTFromFileFooter(nbtCount, entitiesCount, biomeLength);
} }
} else if (braf.length() - headerSize == ((long) getVolume() << 1) + biomeLength) { } else if (canHaveBiomes && braf.length() - headerSize == ((long) getVolume() << 1) + biomeLength) {
hasBiomes = true; hasBiomes = true;
} }
getAndSetOffsetAndOrigin(); getAndSetOffsetAndOrigin();
} catch (IOException e) { } catch (IOException e) {
close();
throw new RuntimeException(e); throw new RuntimeException(e);
} catch (Throwable t) {
close();
throw t;
}
}
private void checkFileLength(File file) throws IOException {
long expectedFileSize = headerSize + ((long) getVolume() << 1);
if (file.length() > Integer.MAX_VALUE) {
if (expectedFileSize >= Integer.MAX_VALUE) {
throw new IOException(String.format(
"Cannot load clipboard of file size: %d > 2147483647 bytes (2.147 GiB), " + "volume: %d blocks",
file.length(),
getVolume()
));
} else {
throw new IOException(String.format(
"Cannot load clipboard of file size > 2147483647 bytes (2.147 GiB). Possible corrupt file? Mismatch" +
" between volume `%d` and file length `%d`!",
file.length(),
getVolume()
));
}
} else if (expectedFileSize != file.length()) {
throw new IOException(String.format(
"Possible corrupt clipboard file? Mismatch between expected file size `%d` and actual file size `%d`!",
expectedFileSize,
file.length()
));
} }
} }
@ -486,13 +524,26 @@ public class DiskOptimizedClipboard extends LinearClipboard {
fileChannel.close(); fileChannel.close();
braf.close(); braf.close();
file.setWritable(true); file.setWritable(true);
closeDirectBuffer(byteBuffer); MappedByteBuffer tmpBuffer = byteBuffer;
byteBuffer = null; byteBuffer = null;
closeDirectBuffer(tmpBuffer);
fileChannel = null; fileChannel = null;
braf = null; braf = null;
} else if (fileChannel != null) {
fileChannel.close();
fileChannel = null;
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
if (fileChannel != null) {
try {
fileChannel.close();
fileChannel = null;
} catch (IOException ex) {
LOGGER.error("Could not close file channel on clipboard {}. If this belongs to a player, the server may " +
"need to be restarted for clipboard use to work.", getFile().getName(), ex);
}
}
} }
} }
@ -648,8 +699,6 @@ public class DiskOptimizedClipboard extends LinearClipboard {
char ordinal = byteBuffer.getChar(diskIndex); char ordinal = byteBuffer.getChar(diskIndex);
return BlockState.getFromOrdinal(ordinal); return BlockState.getFromOrdinal(ordinal);
} catch (IndexOutOfBoundsException ignored) { } catch (IndexOutOfBoundsException ignored) {
} catch (Exception e) {
e.printStackTrace();
} }
return BlockTypes.AIR.getDefaultState(); return BlockTypes.AIR.getDefaultState();
} }