diff --git a/src/main/java/com/seibel/lod/handlers/LodQuadTreeDimensionFileHandler.java b/src/main/java/com/seibel/lod/handlers/LodQuadTreeDimensionFileHandler.java index f2a9b2320..7337e0359 100644 --- a/src/main/java/com/seibel/lod/handlers/LodQuadTreeDimensionFileHandler.java +++ b/src/main/java/com/seibel/lod/handlers/LodQuadTreeDimensionFileHandler.java @@ -17,11 +17,7 @@ */ package com.seibel.lod.handlers; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; +import java.io.*; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; @@ -114,100 +110,105 @@ public class LodQuadTreeDimensionFileHandler int regionZ = regionPos.z; String fileName = getFileNameAndPathForRegion(regionX, regionZ); - - File f = new File(fileName); - - if (!f.exists()) - { - // there wasn't a file, don't - // return anything - return null; - } - - List dataList = new ArrayList<>(); - try - { - BufferedReader br = new BufferedReader(new FileReader(f)); - String s = br.readLine(); - int fileVersion = -1; - - if(s != null && !s.isEmpty()) - { - // try to get the file version - try - { - fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim()); - } - catch(NumberFormatException | StringIndexOutOfBoundsException e) - { - // this file doesn't have a version - // keep the version as -1 - fileVersion = -1; - } - - // check if this file can be read by this file handler - if(fileVersion < LOD_SAVE_FILE_VERSION) - { - // the file we are reading is an older version, - // close the reader and delete the file. - br.close(); - f.delete(); - ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion + - ", version requested: " + LOD_SAVE_FILE_VERSION + - " File was been deleted."); - - return null; - } - else if(fileVersion > LOD_SAVE_FILE_VERSION) - { - // the file we are reading is a newer version, - // close the reader and ignore the file, we don't - // want to accidently delete anything the user may want. - br.close(); - ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion + - ", version requested: " + LOD_SAVE_FILE_VERSION + - " this region will not be written to in order to protect the newer file."); - - return null; - } + if(FILE_EXTENSION == ".bin"){ + try { + ObjectInputStream is = new ObjectInputStream(new FileInputStream(fileName)); + List dataList = (List) is.readObject(); + //LodQuadTree region = (LodQuadTree) is.readObject(); + is.close(); + return new LodQuadTree(dataList, regionX, regionZ); + //return region; + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); } - else - { - // there is no data in this file - br.close(); + return new LodQuadTree(new ArrayList<>(), regionX, regionZ); + //return null; + } + + if(FILE_EXTENSION == ".txt") { + File f = new File(fileName); + + if (!f.exists()) { + // there wasn't a file, don't + // return anything return null; } - - - // this file is a readable version, begin reading the file - s = br.readLine(); - - while(s != null && !s.isEmpty()) - { - try - { - dataList.add(new LodQuadTreeNode(s)); + + List dataList = new ArrayList<>(); + try { + BufferedReader br = new BufferedReader(new FileReader(f)); + String s = br.readLine(); + int fileVersion = -1; + + if (s != null && !s.isEmpty()) { + // try to get the file version + try { + fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim()); + } catch (NumberFormatException | StringIndexOutOfBoundsException e) { + // this file doesn't have a version + // keep the version as -1 + fileVersion = -1; + } + + // check if this file can be read by this file handler + if (fileVersion < LOD_SAVE_FILE_VERSION) { + // the file we are reading is an older version, + // close the reader and delete the file. + br.close(); + f.delete(); + ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion + + ", version requested: " + LOD_SAVE_FILE_VERSION + + " File was been deleted."); + + return null; + } else if (fileVersion > LOD_SAVE_FILE_VERSION) { + // the file we are reading is a newer version, + // close the reader and ignore the file, we don't + // want to accidently delete anything the user may want. + br.close(); + ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion + + ", version requested: " + LOD_SAVE_FILE_VERSION + + " this region will not be written to in order to protect the newer file."); + + return null; + } + } else { + // there is no data in this file + br.close(); + return null; } - catch(IllegalArgumentException e) - { - // we were unable to create this chunk - // for whatever reason. - // skip to the next chunk - ClientProxy.LOGGER.warn(e.getMessage()); - } - + + + // this file is a readable version, begin reading the file s = br.readLine(); + + while (s != null && !s.isEmpty()) { + try { + dataList.add(new LodQuadTreeNode(s)); + } catch (IllegalArgumentException e) { + // we were unable to create this chunk + // for whatever reason. + // skip to the next chunk + ClientProxy.LOGGER.warn(e.getMessage()); + } + + s = br.readLine(); + } + + br.close(); + } catch (IOException e) { + // the buffered reader encountered a + // problem reading the file + return null; } - - br.close(); + return new LodQuadTree(dataList, regionX, regionZ); } - catch (IOException e) - { - // the buffered reader encountered a - // problem reading the file - return null; - } - return new LodQuadTree(dataList, regionX, regionZ); + + return null; } @@ -257,85 +258,86 @@ public class LodQuadTreeDimensionFileHandler // convert to region coordinates int x = region.getLodNodeData().posX; int z = region.getLodNodeData().posZ; - - + + File oldFile = new File(getFileNameAndPathForRegion(x, z)); - try - { - // make sure the file and folder exists - if (!oldFile.exists()) - { - // the file doesn't exist, - // create it and the folder if need be - if(!oldFile.getParentFile().exists()) - oldFile.getParentFile().mkdirs(); - oldFile.createNewFile(); + if(FILE_EXTENSION == ".bin"){ + try { + ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(getFileNameAndPathForRegion(x, z))); + os.writeObject(region.getNodeListWithMask(LodQuadTreeDimension.FULL_COMPLEXITY_MASK, false, true)); + //os.writeObject(region); + os.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); } - else - { - // the file exists, make sure it - // is the correct version. - // (to make sure we don't overwrite a newer - // version file if it exists) - - BufferedReader br = new BufferedReader(new FileReader(oldFile)); - String s = br.readLine(); - int fileVersion = LOD_SAVE_FILE_VERSION; - - if(s != null && !s.isEmpty()) - { - // try to get the file version - try - { - fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim()); - } - catch(NumberFormatException | StringIndexOutOfBoundsException e) - { - // this file doesn't have a correctly formated version - // just overwrite the file - } - } - br.close(); - - // check if this file can be written to by the file handler - if(fileVersion <= LOD_SAVE_FILE_VERSION) - { - // we are good to continue and overwrite the old file - } - else //if(fileVersion > LOD_SAVE_FILE_VERSION) - { - // the file we are reading is a newer version, - // don't write anything, we don't want to accidently - // delete anything the user may want. - return; - } - } - - // the old file is good, now create a new save file - File newFile = new File(getFileNameAndPathForRegion(x, z) + TMP_FILE_EXTENSION); - - FileWriter fw = new FileWriter(newFile); - - // add the version of this file - fw.write(LOD_FILE_VERSION_PREFIX + " " + LOD_SAVE_FILE_VERSION + "\n"); - - // add each LodChunk to the file - List nodesToSave = Collections.unmodifiableList(region.getNodeListWithMask(LodQuadTreeDimension.FULL_COMPLEXITY_MASK, false, true)); - for (LodQuadTreeNode lodQuadTreeNode : nodesToSave) - { - fw.write(lodQuadTreeNode.toData() + "\n"); - lodQuadTreeNode.dirty = false; - } - fw.close(); - - // overwrite the old file with the new one - Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); } - catch(Exception e) - { - ClientProxy.LOGGER.error("LOD file write error: "); - e.printStackTrace(); + if(FILE_EXTENSION == ".txt") { + try { + // make sure the file and folder exists + if (!oldFile.exists()) { + // the file doesn't exist, + // create it and the folder if need be + if (!oldFile.getParentFile().exists()) + oldFile.getParentFile().mkdirs(); + oldFile.createNewFile(); + } else { + // the file exists, make sure it + // is the correct version. + // (to make sure we don't overwrite a newer + // version file if it exists) + + BufferedReader br = new BufferedReader(new FileReader(oldFile)); + String s = br.readLine(); + int fileVersion = LOD_SAVE_FILE_VERSION; + + if (s != null && !s.isEmpty()) { + // try to get the file version + try { + fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim()); + } catch (NumberFormatException | StringIndexOutOfBoundsException e) { + // this file doesn't have a correctly formated version + // just overwrite the file + } + } + br.close(); + + // check if this file can be written to by the file handler + if (fileVersion <= LOD_SAVE_FILE_VERSION) { + // we are good to continue and overwrite the old file + } else //if(fileVersion > LOD_SAVE_FILE_VERSION) + { + // the file we are reading is a newer version, + // don't write anything, we don't want to accidently + // delete anything the user may want. + return; + } + } + + // the old file is good, now create a new save file + File newFile = new File(getFileNameAndPathForRegion(x, z) + TMP_FILE_EXTENSION); + + FileWriter fw = new FileWriter(newFile); + + // add the version of this file + fw.write(LOD_FILE_VERSION_PREFIX + " " + LOD_SAVE_FILE_VERSION + "\n"); + + // add each LodChunk to the file + List nodesToSave = Collections.unmodifiableList(region.getNodeListWithMask(LodQuadTreeDimension.FULL_COMPLEXITY_MASK, false, true)); + for (LodQuadTreeNode lodQuadTreeNode : nodesToSave) { + fw.write(lodQuadTreeNode.toData() + "\n"); + lodQuadTreeNode.dirty = false; + } + fw.close(); + + // overwrite the old file with the new one + Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); + } catch (Exception e) { + ClientProxy.LOGGER.error("LOD file write error: "); + e.printStackTrace(); + } } } diff --git a/src/main/java/com/seibel/lod/objects/LodDataPoint.java b/src/main/java/com/seibel/lod/objects/LodDataPoint.java index ca146c83e..fe698c55d 100644 --- a/src/main/java/com/seibel/lod/objects/LodDataPoint.java +++ b/src/main/java/com/seibel/lod/objects/LodDataPoint.java @@ -17,7 +17,8 @@ */ package com.seibel.lod.objects; -import java.awt.Color; +import java.awt.*; +import java.io.Serializable; import java.util.Objects; import com.seibel.lod.handlers.LodQuadTreeDimensionFileHandler; @@ -30,7 +31,7 @@ import com.seibel.lod.util.LodUtil; * @author James Seibel * @version 8-8-2021 */ -public class LodDataPoint +public class LodDataPoint implements Serializable { /** This is what separates each piece of data in the toData method */ private static final char DATA_DELIMITER = LodQuadTreeDimensionFileHandler.DATA_DELIMITER; diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTree.java b/src/main/java/com/seibel/lod/objects/LodQuadTree.java index caabd8e2d..feb316f20 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTree.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTree.java @@ -17,6 +17,7 @@ */ package com.seibel.lod.objects; +import java.io.Serializable; import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; @@ -32,16 +33,16 @@ import net.minecraft.util.math.ChunkPos; * This object contains all data useful to render LodBlock in a region (32x32 chunk to 512x512 block) * for every node it contains the border of said node, its size, its block position in the world, * color, height and depth. - * + * * @author Leonardo Amato * @author James Seibel * @version 8-7-2021 */ -public class LodQuadTree +public class LodQuadTree implements Serializable { // note: // The term node correspond to a LodQuadTree object - + /* Example on how a LodQuadTreeRegion would be rendered (the number corresponds to the level of the LodNodeData) .___.___._______._______________. @@ -62,16 +63,16 @@ public class LodQuadTree | | | |_______________|_______________| */ - - + + /** If true SetNodesAtLowerLevel will update the color and height of all higher level nodes */ public static boolean UPDATE_HIGHER_LEVEL = true; - + //data useful to render //if children are present then lodNodeData should be a combination of the lodData of the child. This can be //turned off by deselecting the recursive update in all update method. private LodQuadTreeNode lodNode; - + /* .____.____. | NW | NE | | @@ -85,26 +86,26 @@ public class LodQuadTree West - negative x east - positive x */ - - + + /** treeFull is true if and only if all child are not null */ private boolean treeFull; private boolean treeEmpty; - + /** * The four child are based on the four diagonal cardinal directions. * The first index is for North and South and the second index is for East and West.
* Children should always be null for level 0. */ private final LodQuadTree[][] children; - + //parent should always be null for level 9, and always not null for other levels. private final LodQuadTree parent; - - - - - + + + + + /** * Constructor for level 0 without LodNodeData (region level constructor) * @@ -115,7 +116,7 @@ public class LodQuadTree { this(null, new LodQuadTreeNode(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z)); } - + /** * Constructor for generic world without LOD data * @@ -127,7 +128,7 @@ public class LodQuadTree { this(parent, new LodQuadTreeNode(level, regionPos.x, regionPos.z)); } - + /** * Constructor using a dataList * @@ -140,7 +141,7 @@ public class LodQuadTree this(null, new LodQuadTreeNode(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ)); setNodesAtLowerLevel(dataList); } - + /** * Constructor for a generic LodQuadTree using a LodNodeData * @@ -154,8 +155,8 @@ public class LodQuadTree treeEmpty = true; treeFull = false; } - - + + /** * @param dataList list of data to put in the node */ @@ -166,7 +167,7 @@ public class LodQuadTree this.setNodeAtLowerLevel(lodQuadTreeNode); } } - + /** * @param newLowerLodNode data to put in the node * @return true only if the QuadTree has been changed @@ -175,24 +176,24 @@ public class LodQuadTree { byte targetLevel = newLowerLodNode.detailLevel; byte currentLevel = lodNode.detailLevel; - + if (targetLevel >= currentLevel) { // we can't add a node that has a equal or higher // detail level than this region return false; } - + short widthRatio = (short) (lodNode.width / (2 * newLowerLodNode.width)); int WE = Math.abs(Math.floorDiv(newLowerLodNode.posX , widthRatio) % 2); int NS = Math.abs(Math.floorDiv(newLowerLodNode.posZ , widthRatio) % 2); - + if (getChild(NS, WE) == null) { // if this child doesn't exist, create an empty one setChild(NS, WE); } - + LodQuadTree child = getChild(NS, WE); if (lodNode.compareComplexity(newLowerLodNode) > 0) { @@ -214,9 +215,9 @@ public class LodQuadTree return child.setNodeAtLowerLevel(newLowerLodNode); } } - + } - + /** * Gets the LodQuadTreeNode at the given chunkPos. * Returns null if no such LodQuadTreeNode exists. @@ -313,7 +314,7 @@ public class LodQuadTree children[NS][WE] = new LodQuadTree(this, lodNode); } } - + /** * Put an empty child in the given position. * @@ -325,7 +326,7 @@ public class LodQuadTree // TODO is this correctly converting to a regionPos? int childRegionX = lodNode.posX * 2 + WE; int childRegionZ = lodNode.posZ * 2 + NS; - + children[NS][WE] = new LodQuadTree(this, (byte) (lodNode.detailLevel - 1), new RegionPos(childRegionX, childRegionZ)); } @@ -367,7 +368,7 @@ public class LodQuadTree { boolean isFull = true; boolean isEmpty = true; - + // determine if this region is empty or full List dataList = new ArrayList<>(); for (int NS = 0; NS <= 1; NS++) @@ -385,20 +386,20 @@ public class LodQuadTree } } } - + treeFull = isFull; treeEmpty = isEmpty; - + // update this regions lodNode.combineData(dataList); - + // update sub regions if requested if (lodNode.detailLevel < LodUtil.REGION_DETAIL_LEVEL && recursiveUpdate) { this.parent.updateRegion(recursiveUpdate); } } - + /** * Returns nodes that match the given mask. * @@ -407,29 +408,29 @@ public class LodQuadTree * @param getOnlyLeaf if true it will return only leaf nodes * @return list of nodes */ - public List getNodeListWithMask(Set complexityMask, boolean getOnlyDirty, + public List getNodeListWithMask(Set complexityMask, boolean getOnlyDirty, boolean getOnlyLeaf) { List nodeList = new ArrayList<>(); - + if (hasChildren()) { //There is at least 1 child - + // this detail level's node if (!getOnlyLeaf && !(getOnlyDirty && !lodNode.isDirty()) - && complexityMask.contains(lodNode.complexity)) + && complexityMask.contains(lodNode.complexity)) { nodeList.add(lodNode); } - + // search the children for valid nodes for (int NS = 0; NS <= 1; NS++) { for (int WE = 0; WE <= 1; WE++) { LodQuadTree child = children[NS][WE]; - + if (child != null) { nodeList.addAll(child.getNodeListWithMask(complexityMask, getOnlyDirty, getOnlyLeaf)); @@ -440,16 +441,16 @@ public class LodQuadTree else { // This tree has no children - + if (!(getOnlyDirty && !lodNode.isDirty()) && (complexityMask.contains(lodNode.complexity))) { nodeList.add(lodNode); } } - + return nodeList; } - + /** * This method will return all the nodes that can be rendered * @@ -459,31 +460,31 @@ public class LodQuadTree * @param minDistance minimum distance from the player * @return */ - public List getNodeToRender(BlockPos playerPos, int targetLevel, + public List getNodeToRender(BlockPos playerPos, int targetLevel, Set complexityMask, int maxDistance, int minDistance) { int x = playerPos.getX(); int z = playerPos.getZ(); - + List distances = new ArrayList<>(); distances.add((int) Math.sqrt(Math.pow(x - lodNode.getStart().getX(), 2) + Math.pow(z - lodNode.getStart().getZ(), 2))); distances.add((int) Math.sqrt(Math.pow(x - lodNode.getStart().getX(), 2) + Math.pow(z - lodNode.getEnd().getZ(), 2))); distances.add((int) Math.sqrt(Math.pow(x - lodNode.getEnd().getX(), 2) + Math.pow(z - lodNode.getStart().getZ(), 2))); distances.add((int) Math.sqrt(Math.pow(x - lodNode.getEnd().getX(), 2) + Math.pow(z - lodNode.getEnd().getZ(), 2))); - + int min = distances.stream().mapToInt(Integer::intValue).min().getAsInt(); int max = distances.stream().mapToInt(Integer::intValue).max().getAsInt(); List nodeList = new ArrayList<>(); - - + + if (targetLevel <= lodNode.detailLevel && ((min <= maxDistance && max >= minDistance))) { // TODO why is !isNodeFull() here? Becouse if a node is not full then at least one child is missing. // if one child is missing then there would be a hole if you try to render all the other child if (targetLevel == lodNode.detailLevel || !isNodeFull()) { - // we have either reached the right detail level or this tree isn't full - + // we have either reached the right detail level or this tree isn't full + if (!lodNode.isVoidNode() && complexityMask.contains(lodNode.complexity)) { // this node isn't void and has the complexity level we are looking for @@ -508,29 +509,29 @@ public class LodQuadTree } return nodeList; } - - + + /** * Returns nodes that should be generated.
* A node is generated only if it has child, is higher than the target level, and in the distance range. */ - public List> getNodesToGenerate(BlockPos playerPos, byte targetLevel, + public List> getNodesToGenerate(BlockPos playerPos, byte targetLevel, DistanceGenerationMode complexityToGenerate, int maxDistance, int minDistance) { int x = playerPos.getX(); int z = playerPos.getZ(); - + List distances = new ArrayList<>(); distances.add((int) Math.sqrt(Math.pow(x - lodNode.getStart().getX(), 2) + Math.pow(z - lodNode.getStart().getZ(), 2))); distances.add((int) Math.sqrt(Math.pow(x - lodNode.getStart().getX(), 2) + Math.pow(z - lodNode.getEnd().getZ(), 2))); distances.add((int) Math.sqrt(Math.pow(x - lodNode.getEnd().getX(), 2) + Math.pow(z - lodNode.getStart().getZ(), 2))); distances.add((int) Math.sqrt(Math.pow(x - lodNode.getEnd().getX(), 2) + Math.pow(z - lodNode.getEnd().getZ(), 2))); - + int min = distances.stream().mapToInt(Integer::intValue).min().getAsInt(); int max = distances.stream().mapToInt(Integer::intValue).max().getAsInt(); List> nodeList = new ArrayList<>(); - - + + // TODO what is the purpose of isCoordianteInLevel? if (targetLevel <= lodNode.detailLevel && ((min <= maxDistance && max >= minDistance) || isCoordinateInQuadTree(playerPos))) { @@ -545,7 +546,7 @@ public class LodQuadTree else { // check if there are nodes further down that need generation - + for (int NS = 0; NS <= 1; NS++) { for (int WE = 0; WE <= 1; WE++) @@ -554,18 +555,18 @@ public class LodQuadTree { setChild(NS, WE); } - + nodeList.addAll(getChild(NS, WE).getNodesToGenerate(playerPos, targetLevel, complexityToGenerate, maxDistance, minDistance)); } } } } - + return nodeList; } - + /** - * setter for lodNodeData, to maintain a correct relationship between worlds + * setter for lodNodeData, to maintain a correct relationship between worlds * this method forces an update on all parent nodes. * * @param newLodQuadTreeNode data to set @@ -580,14 +581,14 @@ public class LodQuadTree { this.lodNode.updateData(newLodQuadTreeNode); } - + //a recursive update is necessary to change the higher levels - if (parent != null && UPDATE_HIGHER_LEVEL) + if (parent != null && UPDATE_HIGHER_LEVEL) { parent.updateRegion(true); } } - + /** * Returns if the given BlockPos is within the boundary of * this LodQuadTree. @@ -599,50 +600,50 @@ public class LodQuadTree lodNode.getEnd().getX() * lodNode.width >= pos.getX() && lodNode.getEnd().getZ() * lodNode.width >= pos.getZ()); } - - - + + + //================// // simple getters // //================// - + public LodQuadTree getChild(int NS, int WE) { return children[NS][WE]; } - + public LodQuadTreeNode getLodNodeData() { return lodNode; } - + public boolean isNodeFull() { return treeFull; } - + public boolean hasChildren() { return !treeEmpty; } - + public boolean isRenderable() { return (lodNode != null); } - - + + @Override public String toString() { String s = lodNode.toString(); - - s += treeFull ? "Full and " : ""; - s += treeEmpty ? "Empty " : ""; - + + s += treeFull ? "Full and " : ""; + s += treeEmpty ? "Empty " : ""; + if (lodNode != null) - s += "detail: " + lodNode.detailLevel; - + s += "detail: " + lodNode.detailLevel; + /* if(hasChildren()) { @@ -657,8 +658,8 @@ public class LodQuadTree } } } - */ - + */ + return s; } } \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java b/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java index aa22bb842..c5fef38a6 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java @@ -18,6 +18,7 @@ package com.seibel.lod.objects; import java.awt.Color; +import java.io.Serializable; import java.util.List; import java.util.Objects; @@ -35,7 +36,7 @@ import net.minecraft.world.gen.Heightmap; * @author James Seibel * @version 8-8-2021 */ -public class LodQuadTreeNode +public class LodQuadTreeNode implements Serializable { /** This is what separates each piece of data in the toData method */ private static final char DATA_DELIMITER = LodQuadTreeDimensionFileHandler.DATA_DELIMITER;