Newer
Older
void-pack-super-server / work / decompile-ee3ecae0 / net / minecraft / server / RegionFile.java
package net.minecraft.server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import javax.annotation.Nullable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class RegionFile implements AutoCloseable {

    private static final Logger LOGGER = LogManager.getLogger();
    private static final ByteBuffer b = ByteBuffer.allocateDirect(1);
    private final FileChannel dataFile;
    private final java.nio.file.Path d;
    private final RegionFileCompression e;
    private final ByteBuffer f;
    private final IntBuffer g;
    private final IntBuffer h;
    private final RegionFileBitSet freeSectors;

    public RegionFile(File file, File file1) throws IOException {
        this(file.toPath(), file1.toPath(), RegionFileCompression.b);
    }

    public RegionFile(java.nio.file.Path java_nio_file_path, java.nio.file.Path java_nio_file_path1, RegionFileCompression regionfilecompression) throws IOException {
        this.f = ByteBuffer.allocateDirect(8192);
        this.freeSectors = new RegionFileBitSet();
        this.e = regionfilecompression;
        if (!Files.isDirectory(java_nio_file_path1, new LinkOption[0])) {
            throw new IllegalArgumentException("Expected directory, got " + java_nio_file_path1.toAbsolutePath());
        } else {
            this.d = java_nio_file_path1;
            this.g = this.f.asIntBuffer();
            this.g.limit(1024);
            this.f.position(4096);
            this.h = this.f.asIntBuffer();
            this.dataFile = FileChannel.open(java_nio_file_path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
            this.freeSectors.a(0, 2);
            this.f.position(0);
            int i = this.dataFile.read(this.f, 0L);

            if (i != -1) {
                if (i != 8192) {
                    RegionFile.LOGGER.warn("Region file {} has truncated header: {}", java_nio_file_path, i);
                }

                for (int j = 0; j < 1024; ++j) {
                    int k = this.g.get(j);

                    if (k != 0) {
                        int l = b(k);
                        int i1 = a(k);

                        this.freeSectors.a(l, i1);
                    }
                }
            }

        }
    }

    private java.nio.file.Path e(ChunkCoordIntPair chunkcoordintpair) {
        String s = "c." + chunkcoordintpair.x + "." + chunkcoordintpair.z + ".mcc";

        return this.d.resolve(s);
    }

    @Nullable
    public synchronized DataInputStream a(ChunkCoordIntPair chunkcoordintpair) throws IOException {
        int i = this.getOffset(chunkcoordintpair);

        if (i == 0) {
            return null;
        } else {
            int j = b(i);
            int k = a(i);
            int l = k * 4096;
            ByteBuffer bytebuffer = ByteBuffer.allocate(l);

            this.dataFile.read(bytebuffer, (long) (j * 4096));
            bytebuffer.flip();
            if (bytebuffer.remaining() < 5) {
                RegionFile.LOGGER.error("Chunk {} header is truncated: expected {} but read {}", chunkcoordintpair, l, bytebuffer.remaining());
                return null;
            } else {
                int i1 = bytebuffer.getInt();
                byte b0 = bytebuffer.get();

                if (i1 == 0) {
                    RegionFile.LOGGER.warn("Chunk {} is allocated, but stream is missing", chunkcoordintpair);
                    return null;
                } else {
                    int j1 = i1 - 1;

                    if (a(b0)) {
                        if (j1 != 0) {
                            RegionFile.LOGGER.warn("Chunk has both internal and external streams");
                        }

                        return this.a(chunkcoordintpair, b(b0));
                    } else if (j1 > bytebuffer.remaining()) {
                        RegionFile.LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", chunkcoordintpair, j1, bytebuffer.remaining());
                        return null;
                    } else if (j1 < 0) {
                        RegionFile.LOGGER.error("Declared size {} of chunk {} is negative", i1, chunkcoordintpair);
                        return null;
                    } else {
                        return this.a(chunkcoordintpair, b0, a(bytebuffer, j1));
                    }
                }
            }
        }
    }

    private static boolean a(byte b0) {
        return (b0 & 128) != 0;
    }

    private static byte b(byte b0) {
        return (byte) (b0 & -129);
    }

    @Nullable
    private DataInputStream a(ChunkCoordIntPair chunkcoordintpair, byte b0, InputStream inputstream) throws IOException {
        RegionFileCompression regionfilecompression = RegionFileCompression.a(b0);

        if (regionfilecompression == null) {
            RegionFile.LOGGER.error("Chunk {} has invalid chunk stream version {}", chunkcoordintpair, b0);
            return null;
        } else {
            return new DataInputStream(new BufferedInputStream(regionfilecompression.a(inputstream)));
        }
    }

    @Nullable
    private DataInputStream a(ChunkCoordIntPair chunkcoordintpair, byte b0) throws IOException {
        java.nio.file.Path java_nio_file_path = this.e(chunkcoordintpair);

        if (!Files.isRegularFile(java_nio_file_path, new LinkOption[0])) {
            RegionFile.LOGGER.error("External chunk path {} is not file", java_nio_file_path);
            return null;
        } else {
            return this.a(chunkcoordintpair, b0, Files.newInputStream(java_nio_file_path));
        }
    }

    private static ByteArrayInputStream a(ByteBuffer bytebuffer, int i) {
        return new ByteArrayInputStream(bytebuffer.array(), bytebuffer.position(), i);
    }

    private int a(int i, int j) {
        return i << 8 | j;
    }

    private static int a(int i) {
        return i & 255;
    }

    private static int b(int i) {
        return i >> 8;
    }

    private static int c(int i) {
        return (i + 4096 - 1) / 4096;
    }

    public boolean b(ChunkCoordIntPair chunkcoordintpair) {
        int i = this.getOffset(chunkcoordintpair);

        if (i == 0) {
            return false;
        } else {
            int j = b(i);
            int k = a(i);
            ByteBuffer bytebuffer = ByteBuffer.allocate(5);

            try {
                this.dataFile.read(bytebuffer, (long) (j * 4096));
                bytebuffer.flip();
                if (bytebuffer.remaining() != 5) {
                    return false;
                } else {
                    int l = bytebuffer.getInt();
                    byte b0 = bytebuffer.get();

                    if (a(b0)) {
                        if (!RegionFileCompression.b(b(b0))) {
                            return false;
                        }

                        if (!Files.isRegularFile(this.e(chunkcoordintpair), new LinkOption[0])) {
                            return false;
                        }
                    } else {
                        if (!RegionFileCompression.b(b0)) {
                            return false;
                        }

                        if (l == 0) {
                            return false;
                        }

                        int i1 = l - 1;

                        if (i1 < 0 || i1 > 4096 * k) {
                            return false;
                        }
                    }

                    return true;
                }
            } catch (IOException ioexception) {
                return false;
            }
        }
    }

    public DataOutputStream c(ChunkCoordIntPair chunkcoordintpair) throws IOException {
        return new DataOutputStream(new BufferedOutputStream(this.e.a((OutputStream) (new RegionFile.ChunkBuffer(chunkcoordintpair)))));
    }

    protected synchronized void a(ChunkCoordIntPair chunkcoordintpair, ByteBuffer bytebuffer) throws IOException {
        int i = g(chunkcoordintpair);
        int j = this.g.get(i);
        int k = b(j);
        int l = a(j);
        int i1 = bytebuffer.remaining();
        int j1 = c(i1);
        int k1;
        RegionFile.b regionfile_b;

        if (j1 >= 256) {
            java.nio.file.Path java_nio_file_path = this.e(chunkcoordintpair);

            RegionFile.LOGGER.warn("Saving oversized chunk {} ({} bytes} to external file {}", chunkcoordintpair, i1, java_nio_file_path);
            j1 = 1;
            k1 = this.freeSectors.a(j1);
            regionfile_b = this.a(java_nio_file_path, bytebuffer);
            ByteBuffer bytebuffer1 = this.a();

            this.dataFile.write(bytebuffer1, (long) (k1 * 4096));
        } else {
            k1 = this.freeSectors.a(j1);
            regionfile_b = () -> {
                Files.deleteIfExists(this.e(chunkcoordintpair));
            };
            this.dataFile.write(bytebuffer, (long) (k1 * 4096));
        }

        int l1 = (int) (SystemUtils.getTimeMillis() / 1000L);

        this.g.put(i, this.a(k1, j1));
        this.h.put(i, l1);
        this.b();
        regionfile_b.run();
        if (k != 0) {
            this.freeSectors.b(k, l);
        }

    }

    private ByteBuffer a() {
        ByteBuffer bytebuffer = ByteBuffer.allocate(5);

        bytebuffer.putInt(1);
        bytebuffer.put((byte) (this.e.a() | 128));
        bytebuffer.flip();
        return bytebuffer;
    }

    private RegionFile.b a(java.nio.file.Path java_nio_file_path, ByteBuffer bytebuffer) throws IOException {
        java.nio.file.Path java_nio_file_path1 = Files.createTempFile(this.d, "tmp", (String) null);
        FileChannel filechannel = FileChannel.open(java_nio_file_path1, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
        Throwable throwable = null;

        try {
            bytebuffer.position(5);
            filechannel.write(bytebuffer);
        } catch (Throwable throwable1) {
            throwable = throwable1;
            throw throwable1;
        } finally {
            if (filechannel != null) {
                if (throwable != null) {
                    try {
                        filechannel.close();
                    } catch (Throwable throwable2) {
                        throwable.addSuppressed(throwable2);
                    }
                } else {
                    filechannel.close();
                }
            }

        }

        return () -> {
            Files.move(java_nio_file_path1, java_nio_file_path, StandardCopyOption.REPLACE_EXISTING);
        };
    }

    private void b() throws IOException {
        this.f.position(0);
        this.dataFile.write(this.f, 0L);
    }

    private int getOffset(ChunkCoordIntPair chunkcoordintpair) {
        return this.g.get(g(chunkcoordintpair));
    }

    public boolean chunkExists(ChunkCoordIntPair chunkcoordintpair) {
        return this.getOffset(chunkcoordintpair) != 0;
    }

    private static int g(ChunkCoordIntPair chunkcoordintpair) {
        return chunkcoordintpair.j() + chunkcoordintpair.k() * 32;
    }

    public void close() throws IOException {
        try {
            this.c();
        } finally {
            try {
                this.b();
            } finally {
                try {
                    this.dataFile.force(true);
                } finally {
                    this.dataFile.close();
                }
            }
        }

    }

    private void c() throws IOException {
        int i = (int) this.dataFile.size();
        int j = c(i) * 4096;

        if (i != j) {
            ByteBuffer bytebuffer = RegionFile.b.duplicate();

            bytebuffer.position(0);
            this.dataFile.write(bytebuffer, (long) (j - 1));
        }

    }

    interface b {

        void run() throws IOException;
    }

    class ChunkBuffer extends ByteArrayOutputStream {

        private final ChunkCoordIntPair b;

        public ChunkBuffer(ChunkCoordIntPair chunkcoordintpair) {
            super(8096);
            super.write(0);
            super.write(0);
            super.write(0);
            super.write(0);
            super.write(RegionFile.this.e.a());
            this.b = chunkcoordintpair;
        }

        public void close() throws IOException {
            ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count);

            bytebuffer.putInt(0, this.count - 5 + 1);
            RegionFile.this.a(this.b, bytebuffer);
        }
    }
}