Advertisement
Guest User

Untitled

a guest
Jan 23rd, 2020
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.29 KB | None | 0 0
  1. import com.google.common.primitives.Booleans;
  2. import org.apache.commons.lang.ArrayUtils;
  3.  
  4. import java.io.*;
  5. import java.util.Arrays;
  6. import java.util.zip.*;
  7.  
  8. public class RegionFile {
  9.  
  10. private static final int VERSION_GZIP = 1;
  11. private static final int VERSION_DEFLATE = 2;
  12.  
  13. private static final int SECTOR_BYTES = 4096;
  14. private static final int SECTOR_INTS = SECTOR_BYTES / 4;
  15.  
  16. static final int CHUNK_HEADER_SIZE = 5;
  17. private static final byte[] EMPTY_SECTOR = new byte[4096];
  18.  
  19. private final File fileName;
  20. private RandomAccessFile file;
  21. private final int[] offsets;
  22. private final int[] chunkTimestamps;
  23. private boolean[] sectorFree;
  24. private int sizeDelta;
  25. private long lastModified = 0;
  26.  
  27. public RegionFile(File path) {
  28. offsets = new int[SECTOR_INTS];
  29. chunkTimestamps = new int[SECTOR_INTS];
  30.  
  31. fileName = path;
  32. debugln("REGION LOAD " + fileName);
  33.  
  34. sizeDelta = 0;
  35.  
  36. try {
  37. if (path.exists()) {
  38. lastModified = path.lastModified();
  39. }
  40.  
  41. file = new RandomAccessFile(path, "rw");
  42.  
  43. if (file.length() < SECTOR_BYTES) {
  44. /* we need to write the chunk offset table */
  45. for (int i = 0; i < SECTOR_INTS; ++i) {
  46. file.writeInt(0);
  47. }
  48. // write another sector for the timestamp info
  49. for (int i = 0; i < SECTOR_INTS; ++i) {
  50. file.writeInt(0);
  51. }
  52.  
  53. sizeDelta += SECTOR_BYTES * 2;
  54. }
  55.  
  56. if ((file.length() & 0xfff) != 0) {
  57. /* the file size is not a multiple of 4KB, grow it */
  58. for (int i = 0; i < (file.length() & 0xfff); ++i) {
  59. file.write((byte) 0);
  60. }
  61. }
  62.  
  63. /* set up the available sector map */
  64. int nSectors = (int) file.length() / SECTOR_BYTES;
  65. sectorFree = new boolean[nSectors];
  66.  
  67. Arrays.fill(this.sectorFree, true);
  68.  
  69. sectorFree[0] = false; // chunk offset table
  70. sectorFree[1] = false; // for the last modified info
  71.  
  72. file.seek(0);
  73. for (int i = 0; i < SECTOR_INTS; ++i) {
  74. int offset = file.readInt();
  75. offsets[i] = offset;
  76. if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree.length) {
  77. for (int sectorNum = 0; sectorNum < (offset & 0xFF); ++sectorNum) {
  78. sectorFree[(offset >> 8) + sectorNum] = false;
  79. }
  80. }
  81. }
  82. for (int i = 0; i < SECTOR_INTS; ++i) {
  83. int lastModValue = file.readInt();
  84. chunkTimestamps[i] = lastModValue;
  85. }
  86. } catch (IOException e) {
  87. e.printStackTrace();
  88. }
  89. }
  90.  
  91. /* the modification date of the region file when it was first opened */
  92. public long lastModified() {
  93. return lastModified;
  94. }
  95.  
  96. /* gets how much the region file has grown since it was last checked */
  97. public synchronized int getSizeDelta() {
  98. int ret = sizeDelta;
  99. sizeDelta = 0;
  100. return ret;
  101. }
  102.  
  103. // various small debug printing helpers
  104. private void debug(String in) {
  105. // System.out.print(in);
  106. }
  107.  
  108. private void debugln(String in) {
  109. debug(in + "\n");
  110. }
  111.  
  112. private void debug(String mode, int x, int z, String in) {
  113. debug("REGION " + mode + " " + fileName.getName() + "[" + x + "," + z + "] = " + in);
  114. }
  115.  
  116. private void debug(String mode, int x, int z, int count, String in) {
  117. debug("REGION " + mode + " " + fileName.getName() + "[" + x + "," + z + "] " + count + "B = " + in);
  118. }
  119.  
  120. private void debugln(String mode, int x, int z, String in) {
  121. debug(mode, x, z, in + "\n");
  122. }
  123.  
  124. /*
  125. * gets an (uncompressed) stream representing the chunk data returns null if
  126. * the chunk is not found or an error occurs
  127. */
  128. public synchronized DataInputStream getChunkDataInputStream(int x, int z) {
  129. if (outOfBounds(x, z)) {
  130. debugln("READ", x, z, "out of bounds");
  131. return null;
  132. }
  133.  
  134. try {
  135. int offset = getOffset(x, z);
  136. if (offset == 0) {
  137. // debugln("READ", x, z, "miss");
  138. return null;
  139. }
  140.  
  141. int sectorNumber = offset >> 8;
  142. int numSectors = offset & 0xFF;
  143.  
  144. if (sectorNumber + numSectors > sectorFree.length) {
  145. debugln("READ", x, z, "invalid sector");
  146. return null;
  147. }
  148.  
  149. file.seek(sectorNumber * SECTOR_BYTES);
  150. int length = file.readInt();
  151.  
  152. if (length > SECTOR_BYTES * numSectors) {
  153. debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors);
  154. return null;
  155. }
  156.  
  157. byte version = file.readByte();
  158. if (version == VERSION_GZIP) {
  159. byte[] data = new byte[length - 1];
  160. file.read(data);
  161. DataInputStream ret = new DataInputStream(new GZIPInputStream(new ByteArrayInputStream(data)));
  162. // debug("READ", x, z, " = found");
  163. //System.out.println("GZIP");
  164. return ret;
  165. } else if (version == VERSION_DEFLATE) {
  166. byte[] data = new byte[length - 1];
  167. file.read(data);
  168. DataInputStream ret = new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(data)));
  169. // debug("READ", x, z, " = found");
  170. //System.out.println("DEFLATE");
  171. return ret;
  172. }
  173.  
  174. debugln("READ", x, z, "unknown version " + version);
  175. return null;
  176. } catch (IOException e) {
  177. debugln("READ", x, z, "exception");
  178. return null;
  179. }
  180. }
  181.  
  182. public DataOutputStream getChunkDataOutputStream(int x, int z) {
  183. if (outOfBounds(x, z)) return null;
  184.  
  185. return new DataOutputStream(new DeflaterOutputStream(new ChunkBuffer(x, z)));
  186. }
  187.  
  188. /*
  189. * lets chunk writing be multithreaded by not locking the whole file as a
  190. * chunk is serializing -- only writes when serialization is over
  191. */
  192. class ChunkBuffer extends ByteArrayOutputStream {
  193. private final int x;
  194. private final int z;
  195.  
  196. public ChunkBuffer(int x, int z) {
  197. super(8096); // initialize to 8KB
  198. this.x = x;
  199. this.z = z;
  200. }
  201.  
  202. public void close() {
  203. RegionFile.this.write(x, z, buf, count);
  204. }
  205. }
  206.  
  207. /* write a chunk at (x,z) with length bytes of data to disk */
  208. protected synchronized void write(int x, int z, byte[] data, int length) {
  209. try {
  210. int offset = getOffset(x, z);
  211. int sectorNumber = offset >> 8;
  212. int sectorsAllocated = offset & 0xFF;
  213. int sectorsNeeded = (length + CHUNK_HEADER_SIZE) / SECTOR_BYTES + 1;
  214.  
  215. // maximum chunk size is 1MB
  216. if (sectorsNeeded >= 256) {
  217. return;
  218. }
  219.  
  220. if (sectorNumber != 0 && sectorsAllocated == sectorsNeeded) {
  221. /* we can simply overwrite the old sectors */
  222. debug("SAVE", x, z, length, "rewrite");
  223. write(sectorNumber, data, length);
  224. } else {
  225. /* we need to allocate new sectors */
  226.  
  227. /* mark the sectors previously used for this chunk as free */
  228. for (int i = 0; i < sectorsAllocated; ++i) {
  229. this.sectorFree[sectorNumber + i] = true;
  230. }
  231.  
  232. /* scan for a free space large enough to store this chunk */
  233. int runStart = Booleans.indexOf(this.sectorFree, true);
  234. int runLength = 0;
  235. if (runStart != -1) {
  236. for (int i = runStart; i < this.sectorFree.length; ++i) {
  237. if (runLength != 0) {
  238. if (this.sectorFree[i]) runLength++;
  239. else runLength = 0;
  240. } else if (this.sectorFree[i]) {
  241. runStart = i;
  242. runLength = 1;
  243. }
  244. if (runLength >= sectorsNeeded) {
  245. break;
  246. }
  247. }
  248. }
  249.  
  250. if (runLength >= sectorsNeeded) {
  251. /* we found a free space large enough */
  252. debug("SAVE", x, z, length, "reuse");
  253. sectorNumber = runStart;
  254. setOffset(x, z, (sectorNumber << 8) | sectorsNeeded);
  255. for (int i = 0; i < sectorsNeeded; ++i) {
  256. this.sectorFree[sectorNumber + i] = false;
  257. }
  258. write(sectorNumber, data, length);
  259. } else {
  260. /*
  261. * no free space large enough found -- we need to grow the
  262. * file
  263. */
  264. debug("SAVE", x, z, length, "grow");
  265. file.seek(file.length());
  266. sectorNumber = sectorFree.length;
  267. for (int i = 0; i < sectorsNeeded; ++i) {
  268. file.write(EMPTY_SECTOR);
  269. this.sectorFree = ArrayUtils.add(this.sectorFree, false);
  270. }
  271. sizeDelta += SECTOR_BYTES * sectorsNeeded;
  272.  
  273. write(sectorNumber, data, length);
  274. setOffset(x, z, (sectorNumber << 8) | sectorsNeeded);
  275. }
  276. }
  277. setTimestamp(x, z, (int) (System.currentTimeMillis() / 1000L));
  278. } catch (IOException e) {
  279. e.printStackTrace();
  280. }
  281. }
  282.  
  283. /* write a chunk data to the region file at specified sector number */
  284. private void write(int sectorNumber, byte[] data, int length) throws IOException {
  285. debugln(" " + sectorNumber);
  286. file.seek(sectorNumber * SECTOR_BYTES);
  287. file.writeInt(length + 1); // chunk length
  288. file.writeByte(VERSION_DEFLATE); // chunk version number
  289. file.write(data, 0, length); // chunk data
  290. }
  291.  
  292. /* is this an invalid chunk coordinate? */
  293. private boolean outOfBounds(int x, int z) {
  294. return x < 0 || x >= 32 || z < 0 || z >= 32;
  295. }
  296.  
  297. private int getOffset(int x, int z) {
  298. return offsets[x + z * 32];
  299. }
  300.  
  301. public boolean hasChunk(int x, int z) {
  302. return getOffset(x, z) != 0;
  303. }
  304.  
  305. private void setOffset(int x, int z, int offset) throws IOException {
  306. offsets[x + z * 32] = offset;
  307. file.seek((x + z * 32) * 4);
  308. file.writeInt(offset);
  309. }
  310.  
  311. private void setTimestamp(int x, int z, int value) throws IOException {
  312. chunkTimestamps[x + z * 32] = value;
  313. file.seek(SECTOR_BYTES + (x + z * 32) * 4);
  314. file.writeInt(value);
  315. }
  316.  
  317. public void close() throws IOException {
  318. file.close();
  319. }
  320. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement