From 508b94ddc351b101cce40514aef8e78a804fc42e Mon Sep 17 00:00:00 2001 From: Jordan Date: Wed, 29 Mar 2023 23:13:50 +0100 Subject: [PATCH] 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 --- .../clipboard/DiskOptimizedClipboard.java | 65 ++++++++++++++++--- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java index acdf9fad7..100e3265f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java @@ -64,9 +64,9 @@ public class DiskOptimizedClipboard extends LinearClipboard { private final int headerSize; private RandomAccessFile braf; - private MappedByteBuffer byteBuffer; + private MappedByteBuffer byteBuffer = null; - private FileChannel fileChannel; + private FileChannel fileChannel = null; private boolean hasBiomes = false; private boolean canHaveBiomes = true; private int nbtBytesRemaining; @@ -133,7 +133,7 @@ public class DiskOptimizedClipboard extends LinearClipboard { e.printStackTrace(); } 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(fileLength); this.nbtBytesRemaining = Integer.MAX_VALUE - (int) fileLength; @@ -144,7 +144,11 @@ public class DiskOptimizedClipboard extends LinearClipboard { byteBuffer.putChar(6, (char) getHeight()); byteBuffer.putChar(8, (char) getLength()); } catch (IOException e) { + close(); throw new RuntimeException(e); + } catch (Throwable t) { + close(); + throw t; } } @@ -169,11 +173,15 @@ public class DiskOptimizedClipboard extends LinearClipboard { nbtMap = new HashMap<>(); try { this.file = file; + checkFileLength(file); this.braf = new RandomAccessFile(file, "rw"); braf.setLength(file.length()); this.nbtBytesRemaining = Integer.MAX_VALUE - (int) file.length(); 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) { readBiomeStatusFromHeader(); int nbtCount = readNBTSavedCountFromHeader(); @@ -181,12 +189,42 @@ public class DiskOptimizedClipboard extends LinearClipboard { if (Settings.settings().CLIPBOARD.SAVE_CLIPBOARD_NBT_TO_DISK && (nbtCount + entitiesCount > 0)) { 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; } getAndSetOffsetAndOrigin(); } catch (IOException e) { + close(); 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(); braf.close(); file.setWritable(true); - closeDirectBuffer(byteBuffer); + MappedByteBuffer tmpBuffer = byteBuffer; byteBuffer = null; + closeDirectBuffer(tmpBuffer); fileChannel = null; braf = null; + } else if (fileChannel != null) { + fileChannel.close(); + fileChannel = null; } } catch (IOException e) { 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); return BlockState.getFromOrdinal(ordinal); } catch (IndexOutOfBoundsException ignored) { - } catch (Exception e) { - e.printStackTrace(); } return BlockTypes.AIR.getDefaultState(); }