HashChunkletManager.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. package com.gmail.nossr50.util.blockmeta;
  2. import java.io.EOFException;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.io.ObjectInputStream;
  8. import java.io.ObjectOutputStream;
  9. import java.io.StreamCorruptedException;
  10. import java.io.UTFDataFormatException;
  11. import java.util.HashMap;
  12. import org.bukkit.Bukkit;
  13. import org.bukkit.World;
  14. import org.bukkit.block.Block;
  15. import com.gmail.nossr50.mcMMO;
  16. public class HashChunkletManager implements ChunkletManager {
  17. private HashMap<String, ChunkletStore> store = new HashMap<String, ChunkletStore>();
  18. @Override
  19. public void loadChunklet(int cx, int cy, int cz, World world) {
  20. File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
  21. File cxDir = new File(dataDir, "" + cx);
  22. if(!cxDir.exists()) return;
  23. File czDir = new File(cxDir, "" + cz);
  24. if(!czDir.exists()) return;
  25. File yFile = new File(czDir, "" + cy);
  26. if(!yFile.exists()) return;
  27. ChunkletStore in = deserializeChunkletStore(yFile);
  28. if(in != null) {
  29. store.put(world.getName() + "," + cx + "," + cz + "," + cy, in);
  30. }
  31. }
  32. @Override
  33. public void chunkLoaded(int cx, int cz, World world) {
  34. //File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
  35. //File cxDir = new File(dataDir, "" + cx);
  36. //if(!cxDir.exists()) return;
  37. //File czDir = new File(cxDir, "" + cz);
  38. //if(!czDir.exists()) return;
  39. //for(int y = 0; y < 4; y++) {
  40. // File yFile = new File(czDir, "" + y);
  41. // if(!yFile.exists()) {
  42. // continue;
  43. // } else {
  44. // ChunkletStore in = deserializeChunkletStore(yFile);
  45. // if(in != null) {
  46. // store.put(world.getName() + "," + cx + "," + cz + "," + y, in);
  47. // }
  48. // }
  49. //}
  50. }
  51. @Override
  52. public void chunkUnloaded(int cx, int cz, World world) {
  53. File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
  54. for(int y = 0; y < 4; y++) {
  55. if(store.containsKey(world.getName() + "," + cx + "," + cz + "," + y)) {
  56. File cxDir = new File(dataDir, "" + cx);
  57. if(!cxDir.exists()) cxDir.mkdir();
  58. File czDir = new File(cxDir, "" + cz);
  59. if(!czDir.exists()) czDir.mkdir();
  60. File yFile = new File(czDir, "" + y);
  61. ChunkletStore out = store.get(world.getName() + "," + cx + "," + cz + "," + y);
  62. serializeChunkletStore(out, yFile);
  63. store.remove(world.getName() + "," + cx + "," + cz + "," + y);
  64. }
  65. }
  66. }
  67. @Override
  68. public void saveWorld(World world) {
  69. String worldName = world.getName();
  70. File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
  71. for(String key : store.keySet()) {
  72. String[] info = key.split(",");
  73. if(worldName.equals(info[0])) {
  74. File cxDir = new File(dataDir, "" + info[1]);
  75. if(!cxDir.exists()) cxDir.mkdir();
  76. File czDir = new File(cxDir, "" + info[2]);
  77. if(!czDir.exists()) czDir.mkdir();
  78. File yFile = new File(czDir, "" + info[3]);
  79. serializeChunkletStore(store.get(key), yFile);
  80. }
  81. }
  82. }
  83. @Override
  84. public void unloadWorld(World world) {
  85. saveWorld(world);
  86. String worldName = world.getName();
  87. for(String key : store.keySet()) {
  88. String tempWorldName = key.split(",")[0];
  89. if(tempWorldName.equals(worldName)) {
  90. store.remove(key);
  91. return;
  92. }
  93. }
  94. }
  95. @Override
  96. public void loadWorld(World world) {
  97. //for(Chunk chunk : world.getLoadedChunks()) {
  98. // this.chunkLoaded(chunk.getX(), chunk.getZ(), world);
  99. //}
  100. }
  101. @Override
  102. public void saveAll() {
  103. for(World world : Bukkit.getWorlds()) {
  104. saveWorld(world);
  105. }
  106. }
  107. @Override
  108. public void unloadAll() {
  109. saveAll();
  110. for(World world : Bukkit.getWorlds()) {
  111. unloadWorld(world);
  112. }
  113. }
  114. @Override
  115. public boolean isTrue(int x, int y, int z, World world) {
  116. int cx = x / 16;
  117. int cz = z / 16;
  118. int cy = y / 64;
  119. String key = world.getName() + "," + cx + "," + cz + "," + cy;
  120. if (!store.containsKey(key)) {
  121. loadChunklet(cx, cy, cz, world);
  122. }
  123. if (!store.containsKey(key)) {
  124. return false;
  125. }
  126. ChunkletStore check = store.get(world.getName() + "," + cx + "," + cz + "," + cy);
  127. int ix = Math.abs(x) % 16;
  128. int iz = Math.abs(z) % 16;
  129. int iy = Math.abs(y) % 64;
  130. return check.isTrue(ix, iy, iz);
  131. }
  132. @Override
  133. public boolean isTrue(Block block) {
  134. return isTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
  135. }
  136. @Override
  137. public void setTrue(int x, int y, int z, World world) {
  138. int cx = x / 16;
  139. int cz = z / 16;
  140. int cy = y / 64;
  141. int ix = Math.abs(x) % 16;
  142. int iz = Math.abs(z) % 16;
  143. int iy = Math.abs(y) % 64;
  144. String key = world.getName() + "," + cx + "," + cz + "," + cy;
  145. if (!store.containsKey(key)) {
  146. loadChunklet(cx, cy, cz, world);
  147. }
  148. ChunkletStore cStore = store.get(key);
  149. if (cStore == null) {
  150. cStore = ChunkletStoreFactory.getChunkletStore();
  151. store.put(world.getName() + "," + cx + "," + cz + "," + cy, cStore);
  152. }
  153. cStore.setTrue(ix, iy, iz);
  154. }
  155. @Override
  156. public void setTrue(Block block) {
  157. setTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
  158. }
  159. @Override
  160. public void setFalse(int x, int y, int z, World world) {
  161. int cx = x / 16;
  162. int cz = z / 16;
  163. int cy = y / 64;
  164. int ix = Math.abs(x) % 16;
  165. int iz = Math.abs(z) % 16;
  166. int iy = Math.abs(y) % 64;
  167. String key = world.getName() + "," + cx + "," + cz + "," + cy;
  168. if (!store.containsKey(key)) {
  169. loadChunklet(cx, cy, cz, world);
  170. }
  171. ChunkletStore cStore = store.get(key);
  172. if (cStore == null) {
  173. return; //No need to make a store for something we will be setting to false
  174. }
  175. cStore.setFalse(ix, iy, iz);
  176. }
  177. @Override
  178. public void setFalse(Block block) {
  179. setFalse(block.getX(), block.getY(), block.getZ(), block.getWorld());
  180. }
  181. @Override
  182. public void cleanUp() {
  183. for(String key : store.keySet()) {
  184. if(store.get(key).isEmpty()) {
  185. String[] info = key.split(",");
  186. File dataDir = new File(Bukkit.getWorld(info[0]).getWorldFolder(), "mcmmo_data");
  187. File cxDir = new File(dataDir, "" + info[1]);
  188. if(!cxDir.exists()) continue;
  189. File czDir = new File(cxDir, "" + info[2]);
  190. if(!czDir.exists()) continue;
  191. File yFile = new File(czDir, "" + info[3]);
  192. yFile.delete();
  193. //Delete empty directories
  194. if(czDir.list().length == 0) czDir.delete();
  195. if(cxDir.list().length == 0) cxDir.delete();
  196. }
  197. }
  198. }
  199. /**
  200. * @param cStore ChunkletStore to save
  201. * @param location Where on the disk to put it
  202. */
  203. private void serializeChunkletStore(ChunkletStore cStore, File location) {
  204. FileOutputStream fileOut = null;
  205. ObjectOutputStream objOut = null;
  206. try {
  207. fileOut = new FileOutputStream(location);
  208. objOut = new ObjectOutputStream(fileOut);
  209. objOut.writeObject(cStore);
  210. }
  211. catch (IOException ex) {
  212. ex.printStackTrace();
  213. }
  214. finally {
  215. if (objOut != null) {
  216. try {
  217. objOut.flush();
  218. objOut.close();
  219. }
  220. catch (IOException ex) {
  221. ex.printStackTrace();
  222. }
  223. }
  224. if (fileOut != null) {
  225. try {
  226. fileOut.close();
  227. }
  228. catch (IOException ex) {
  229. ex.printStackTrace();
  230. }
  231. }
  232. }
  233. }
  234. /**
  235. * @param location Where on the disk to read from
  236. * @return ChunkletStore from the specified location
  237. */
  238. private ChunkletStore deserializeChunkletStore(File location) {
  239. ChunkletStore storeIn = null;
  240. FileInputStream fileIn = null;
  241. ObjectInputStream objIn = null;
  242. try {
  243. fileIn = new FileInputStream(location);
  244. objIn = new ObjectInputStream(fileIn);
  245. storeIn = (ChunkletStore) objIn.readObject();
  246. }
  247. catch (IOException ex) {
  248. if (ex instanceof EOFException) {
  249. // EOF should only happen on Chunklets that somehow have been corrupted.
  250. mcMMO.p.getLogger().severe("Chunklet data at " + location.toString() + " could not be read due to an EOFException, data in this area will be lost.");
  251. return ChunkletStoreFactory.getChunkletStore();
  252. }
  253. else if (ex instanceof StreamCorruptedException) {
  254. // StreamCorrupted happens when the Chunklet is no good.
  255. mcMMO.p.getLogger().severe("Chunklet data at " + location.toString() + " is corrupted, data in this area will be lost.");
  256. return ChunkletStoreFactory.getChunkletStore();
  257. }
  258. else if (ex instanceof UTFDataFormatException) {
  259. // UTF happens when the Chunklet cannot be read or is corrupted
  260. mcMMO.p.getLogger().severe("Chunklet data at " + location.toString() + " could not be read due to an UTFDataFormatException, data in this area will be lost.");
  261. return ChunkletStoreFactory.getChunkletStore();
  262. }
  263. ex.printStackTrace();
  264. }
  265. catch (ClassNotFoundException ex) {
  266. ex.printStackTrace();
  267. }
  268. finally {
  269. if (objIn != null) {
  270. try {
  271. objIn.close();
  272. }
  273. catch (IOException ex) {
  274. ex.printStackTrace();
  275. }
  276. }
  277. if (fileIn != null) {
  278. try {
  279. fileIn.close();
  280. }
  281. catch (IOException ex) {
  282. ex.printStackTrace();
  283. }
  284. }
  285. }
  286. // TODO: Make this less messy, as it is, it's kinda... depressing to do it like this.
  287. // Might also make a mess when we move to stacks, but at that point I think I will write a new Manager...
  288. // IMPORTANT! If ChunkletStoreFactory is going to be returning something other than PrimitiveEx we need to remove this, as it will be breaking time for old maps
  289. if(!(storeIn instanceof PrimitiveExChunkletStore)) {
  290. ChunkletStore tempStore = ChunkletStoreFactory.getChunkletStore();
  291. tempStore.copyFrom(storeIn);
  292. storeIn = tempStore;
  293. }
  294. return storeIn;
  295. }
  296. }