From c110e268b8eb0f963f3de4a163e7c29893fe5e57 Mon Sep 17 00:00:00 2001 From: TomTheFurry Date: Tue, 26 Jul 2022 23:14:28 +0800 Subject: [PATCH] Fixed IO bugs --- .../transform/FullToColumnTransformer.java | 2 +- .../seibel/lod/core/a7/save/io/MetaFile.java | 24 +++++++++++-------- .../core/a7/save/io/file/DataMetaFile.java | 6 +++-- .../core/a7/util/UnclosableInputStream.java | 16 +++++++++++++ .../core/a7/util/UnclosableOutputStream.java | 13 ++++++++++ 5 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/seibel/lod/core/a7/util/UnclosableInputStream.java create mode 100644 src/main/java/com/seibel/lod/core/a7/util/UnclosableOutputStream.java diff --git a/src/main/java/com/seibel/lod/core/a7/datatype/transform/FullToColumnTransformer.java b/src/main/java/com/seibel/lod/core/a7/datatype/transform/FullToColumnTransformer.java index 7d8682dda..6d4387032 100644 --- a/src/main/java/com/seibel/lod/core/a7/datatype/transform/FullToColumnTransformer.java +++ b/src/main/java/com/seibel/lod/core/a7/datatype/transform/FullToColumnTransformer.java @@ -26,7 +26,7 @@ public class FullToColumnTransformer { final int vertSize = Config.Client.Graphics.Quality.verticalQuality.get().calculateMaxVerticalData(data.getDataDetail()); final ColumnRenderSource columnSource = new ColumnRenderSource(pos, vertSize, level.getMinY()); - if (dataDetail == pos.sectionDetail- columnSource.getDataDetail()) { + if (dataDetail == pos.sectionDetail - columnSource.getDataDetail()) { for (int x = 0; x < pos.getWidth(dataDetail).value; x++) { for (int z = 0; z < pos.getWidth(dataDetail).value; z++) { ColumnArrayView columnArrayView = columnSource.getVerticalDataView(x, z); diff --git a/src/main/java/com/seibel/lod/core/a7/save/io/MetaFile.java b/src/main/java/com/seibel/lod/core/a7/save/io/MetaFile.java index 8749466cf..bd5c818f5 100644 --- a/src/main/java/com/seibel/lod/core/a7/save/io/MetaFile.java +++ b/src/main/java/com/seibel/lod/core/a7/save/io/MetaFile.java @@ -13,6 +13,7 @@ import java.util.zip.Adler32; import java.util.zip.CheckedOutputStream; import com.seibel.lod.core.a7.pos.DhSectionPos; +import com.seibel.lod.core.a7.util.UnclosableOutputStream; import com.seibel.lod.core.logging.DhLoggerBuilder; import com.seibel.lod.core.util.LodUtil; import org.apache.logging.log4j.Logger; @@ -36,9 +37,12 @@ public class MetaFile { // // 8 bytes: timestamp - // Total size: 32 bytes + // Used size: 40 bytes + // Remaining space: 24 bytes + // Total size: 64 bytes - public static final int METADATA_SIZE = 32; + public static final int METADATA_SIZE = 64; + public static final int METADATA_RESERVED_SIZE = 24; public static final int METADATA_MAGIC_BYTES = 0x44_48_76_30; public final DhSectionPos pos; @@ -55,8 +59,8 @@ public class MetaFile { // Load a metaFile in this path. It also automatically read the metadata. protected MetaFile(File path) throws IOException { validateFile(); - try (FileInputStream fin = new FileInputStream(path)) { - MappedByteBuffer buffer = fin.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, METADATA_SIZE); + try (FileChannel channel = FileChannel.open(path.toPath(), StandardOpenOption.READ)) { + MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, METADATA_SIZE); this.path = path; int magic = buffer.getInt(); @@ -73,7 +77,7 @@ public class MetaFile { byte unused = buffer.get(); dataTypeId = buffer.getLong(); timestamp = buffer.getLong(); - LodUtil.assertTrue(buffer.remaining() == 0); + LodUtil.assertTrue(buffer.remaining() == METADATA_RESERVED_SIZE); pos = new DhSectionPos(detailLevel, x, z); } } @@ -109,7 +113,7 @@ public class MetaFile { byte unused = buffer.get(); long dataTypeId = buffer.getLong(); long timestamp = buffer.getLong(); - LodUtil.assertTrue(buffer.remaining() == 0); + LodUtil.assertTrue(buffer.remaining() == METADATA_RESERVED_SIZE); DhSectionPos newPos = new DhSectionPos(detailLevel, x, z); if (!newPos.equals(pos)) { @@ -121,14 +125,14 @@ public class MetaFile { protected void writeData(Consumer dataWriter) throws IOException { if (path.exists()) validateFile(); - File tempFile = File.createTempFile("", "tmp", path.getParentFile()); + File tempFile = File.createTempFile("lodDataFile", "tmp", path.getParentFile()); tempFile.deleteOnExit(); try (FileChannel file = FileChannel.open(tempFile.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { { file.position(METADATA_SIZE); int checksum; - try (OutputStream channelOut = Channels.newOutputStream(file); + try (OutputStream channelOut = new UnclosableOutputStream(Channels.newOutputStream(file)); // Prevent closing the channel BufferedOutputStream bufferedOut = new BufferedOutputStream(channelOut); // TODO: Is default buffer size ok? Do we even need to buffer? CheckedOutputStream checkedOut = new CheckedOutputStream(bufferedOut, new Adler32())) { // TODO: Is Adler32 ok? dataWriter.accept(checkedOut); @@ -148,14 +152,14 @@ public class MetaFile { buff.put(Byte.MIN_VALUE); // Unused buff.putLong(dataTypeId); buff.putLong(timestamp); - LodUtil.assertTrue(buff.remaining() == 0); + LodUtil.assertTrue(buff.remaining() == METADATA_RESERVED_SIZE); buff.flip(); file.write(buff); } file.close(); // Atomic move / replace the actual file Files.move(tempFile.toPath(), path.toPath(), StandardCopyOption.REPLACE_EXISTING, - StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.COPY_ATTRIBUTES); + StandardCopyOption.ATOMIC_MOVE); } finally { try { boolean i = tempFile.delete(); // Delete temp file. Ignore errors if fails. diff --git a/src/main/java/com/seibel/lod/core/a7/save/io/file/DataMetaFile.java b/src/main/java/com/seibel/lod/core/a7/save/io/file/DataMetaFile.java index a208f80db..f45baffda 100644 --- a/src/main/java/com/seibel/lod/core/a7/save/io/file/DataMetaFile.java +++ b/src/main/java/com/seibel/lod/core/a7/save/io/file/DataMetaFile.java @@ -175,7 +175,7 @@ public class DataMetaFile extends MetaFile { } else localVer = localVersion.get(); data.setLocalVersion(localVer); // Finally, return the data. - return null; + return data; } private LodDataSource loadFile() { @@ -237,8 +237,10 @@ public class DataMetaFile extends MetaFile { try { dataLevel = data.getDataDetail(); loader = DataSourceLoader.getLoader(data.getClass(), data.getDataVersion()); + // FIXME: Uncomment this and fix id when we have FullDataSource loader! + //LodUtil.assertTrue(loader != null, "No loader for {} (v{})", data.getClass(), data.getDataVersion()); dataType = data.getClass(); - dataTypeId = loader.datatypeId; + dataTypeId = loader == null ? 0 : loader.datatypeId; loaderVersion = data.getDataVersion(); timestamp = System.currentTimeMillis(); // TODO: Do we need to use server synced time? // Warn: This may become an attack vector! Be careful! diff --git a/src/main/java/com/seibel/lod/core/a7/util/UnclosableInputStream.java b/src/main/java/com/seibel/lod/core/a7/util/UnclosableInputStream.java new file mode 100644 index 000000000..3eae49f3a --- /dev/null +++ b/src/main/java/com/seibel/lod/core/a7/util/UnclosableInputStream.java @@ -0,0 +1,16 @@ +package com.seibel.lod.core.a7.util; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class UnclosableInputStream extends FilterInputStream { + public UnclosableInputStream(InputStream it) { + super(it); + } + + @Override + public void close() throws IOException { + // Do nothing. + } +} diff --git a/src/main/java/com/seibel/lod/core/a7/util/UnclosableOutputStream.java b/src/main/java/com/seibel/lod/core/a7/util/UnclosableOutputStream.java new file mode 100644 index 000000000..508e4735f --- /dev/null +++ b/src/main/java/com/seibel/lod/core/a7/util/UnclosableOutputStream.java @@ -0,0 +1,13 @@ +package com.seibel.lod.core.a7.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class UnclosableOutputStream extends FilterOutputStream { + public UnclosableOutputStream(OutputStream it) { + super(it); + } + @Override + public void close() throws IOException {} +}