diff --git a/StevenDimDoors/mod_pocketDim/EventHookContainer.java b/StevenDimDoors/mod_pocketDim/EventHookContainer.java index aeaa0d3..6191018 100644 --- a/StevenDimDoors/mod_pocketDim/EventHookContainer.java +++ b/StevenDimDoors/mod_pocketDim/EventHookContainer.java @@ -89,7 +89,7 @@ public class EventHookContainer } @ForgeSubscribe - public void onWorldsave(WorldEvent.Save event) + public void onWorldSave(WorldEvent.Save event) { if (event.world.provider.dimensionId == 0) { diff --git a/StevenDimDoors/mod_pocketDim/core/DimLink.java b/StevenDimDoors/mod_pocketDim/core/DimLink.java index cdf25a6..f0270a7 100644 --- a/StevenDimDoors/mod_pocketDim/core/DimLink.java +++ b/StevenDimDoors/mod_pocketDim/core/DimLink.java @@ -14,6 +14,12 @@ public abstract class DimLink protected DimLink(Point4D source, DimLink parent) { + if (parent.source.getDimension() != source.getDimension()) + { + // Ban having children in other dimensions to avoid serialization issues with cross-dimensional tails + throw new IllegalArgumentException("source and parent.source must have the same dimension."); + } + this.parent = parent; this.source = source; this.tail = parent.tail; diff --git a/StevenDimDoors/mod_pocketDim/core/NewDimData.java b/StevenDimDoors/mod_pocketDim/core/NewDimData.java index a480333..12c7f25 100644 --- a/StevenDimDoors/mod_pocketDim/core/NewDimData.java +++ b/StevenDimDoors/mod_pocketDim/core/NewDimData.java @@ -57,13 +57,17 @@ public abstract class NewDimData if (nextParent == null) { throw new IllegalArgumentException("nextParent cannot be null."); - } - + } if (this == nextParent) { //Ignore this request silently return false; } + if (nextParent.source.getDimension() != source.getDimension()) + { + // Ban having children in other dimensions to avoid serialization issues with cross-dimensional tails + throw new IllegalArgumentException("source and parent.source must have the same dimension."); + } //Release children for (DimLink child : children) @@ -106,22 +110,22 @@ public abstract class NewDimData } } - private static Random random = new Random(); + protected static Random random = new Random(); - private final int id; - private final Map linkMapping; - private final List linkList; - private final boolean isDungeon; - private boolean isFilled; - private final int depth; - private int packDepth; - private final NewDimData parent; - private final NewDimData root; - private final List children; - private Point4D origin; - private int orientation; - private DungeonData dungeon; - private final IUpdateWatcher linkWatcher; + protected int id; + protected Map linkMapping; + protected List linkList; + protected boolean isDungeon; + protected boolean isFilled; + protected int depth; + protected int packDepth; + protected NewDimData parent; + protected NewDimData root; + protected List children; + protected Point4D origin; + protected int orientation; + protected DungeonData dungeon; + protected IUpdateWatcher linkWatcher; protected NewDimData(int id, NewDimData parent, boolean isPocket, boolean isDungeon, IUpdateWatcher linkWatcher) diff --git a/StevenDimDoors/mod_pocketDim/core/PocketManager.java b/StevenDimDoors/mod_pocketDim/core/PocketManager.java index eda8947..3c992fc 100644 --- a/StevenDimDoors/mod_pocketDim/core/PocketManager.java +++ b/StevenDimDoors/mod_pocketDim/core/PocketManager.java @@ -13,6 +13,9 @@ import net.minecraftforge.common.DimensionManager; import StevenDimDoors.mod_pocketDim.DDProperties; import StevenDimDoors.mod_pocketDim.helpers.Compactor; import StevenDimDoors.mod_pocketDim.helpers.DeleteFolder; +import StevenDimDoors.mod_pocketDim.saving.DDSaveHandler; +import StevenDimDoors.mod_pocketDim.saving.IPackable; +import StevenDimDoors.mod_pocketDim.saving.PackedDimData; import StevenDimDoors.mod_pocketDim.util.Point4D; import StevenDimDoors.mod_pocketDim.watcher.ClientDimData; import StevenDimDoors.mod_pocketDim.watcher.IUpdateSource; @@ -25,7 +28,7 @@ import StevenDimDoors.mod_pocketDim.watcher.UpdateWatcherProxy; */ public class PocketManager { - private static class InnerDimData extends NewDimData + private static class InnerDimData extends NewDimData implements IPackable { // This class allows us to instantiate NewDimData indirectly without exposing // a public constructor from NewDimData. It's meant to stop us from constructing @@ -43,6 +46,49 @@ public class PocketManager // This constructor is meant for client-side code only super(id, root); } + + public void clear() + { + // If this dimension has a parent, remove it from its parent's list of children + if (parent != null) + { + parent.children.remove(this); + } + // Remove this dimension as the parent of its children + for (NewDimData child : children) + { + child.parent = null; + } + // Clear all fields + id = Integer.MIN_VALUE; + linkMapping.clear(); + linkMapping = null; + linkList.clear(); + linkList = null; + children.clear(); + children = null; + isDungeon = false; + isFilled = false; + depth = Integer.MIN_VALUE; + packDepth = Integer.MIN_VALUE; + origin = null; + orientation = Integer.MIN_VALUE; + dungeon = null; + linkWatcher = null; + } + + @Override + public String name() + { + return String.valueOf(id); + } + + @Override + public PackedDimData pack() + { + // FIXME: IMPLEMENTATION PLZTHX + return null; + } } private static class ClientLinkWatcher implements IUpdateWatcher @@ -140,44 +186,44 @@ public class PocketManager isLoading = false; } - public boolean clearPocket(NewDimData dimension) + public boolean resetDungeon(NewDimData target) { - if (!dimension.isPocketDimension() || DimensionManager.getWorld(dimension.id()) != null) + // We can't reset the dimension if it's currently loaded or if it's not a dungeon. + // We cast to InnerDimData so that if anyone tries to be a smartass and create their + // own version of NewDimData, this will throw an exception. + InnerDimData dimension = (InnerDimData) target; + if (dimension.isDungeon() && DimensionManager.getWorld(dimension.id()) == null) { - return false; + File saveDirectory = new File(DimensionManager.getCurrentSaveRootDirectory() + "/DimensionalDoors/pocketDimID" + dimension.id()); + if (DeleteFolder.deleteFolder(saveDirectory)) + { + dimension.setFilled(false); + return true; + } } - - File save = new File(DimensionManager.getCurrentSaveRootDirectory() + "/DimensionalDoors/pocketDimID" + dimension.id()); - DeleteFolder.deleteFolder(save); - dimension.setFilled(false); - //FIXME: Reset door information? - return true; + return false; } - public static boolean deletePocket(NewDimData dimension, boolean deleteFolder) + public static boolean deletePocket(NewDimData target, boolean deleteFolder) { - //FIXME: Shouldn't the links in and out of this dimension be altered somehow? Otherwise we have links pointing - //into a deleted dimension! - - //Checks to see if the pocket is loaded or isn't actually a pocket. + // We can't delete the dimension if it's currently loaded or if it's not actually a pocket. + // We cast to InnerDimData so that if anyone tries to be a smartass and create their + // own version of NewDimData, this will throw an exception. + InnerDimData dimension = (InnerDimData) target; if (dimension.isPocketDimension() && DimensionManager.getWorld(dimension.id()) == null) { - dimensionData.remove(dimension.id()); - DimensionManager.unregisterDimension(dimension.id()); if (deleteFolder) { - File save = new File(DimensionManager.getCurrentSaveRootDirectory() + "/DimensionalDoors/pocketDimID" + dimension.id()); - DeleteFolder.deleteFolder(save); + File saveDirectory = new File(DimensionManager.getCurrentSaveRootDirectory() + "/DimensionalDoors/pocketDimID" + dimension.id()); + DeleteFolder.deleteFolder(saveDirectory); } - //Raise the dim deleted event + dimensionData.remove(dimension.id()); + // Raise the dim deleted event dimWatcher.onDeleted(new ClientDimData(dimension)); - //dimension.implode()??? -- more like delete, but yeah + dimension.clear(); return true; } - else - { - return false; - } + return false; } private static void registerPockets(DDProperties properties) @@ -222,32 +268,29 @@ public class PocketManager * loads the dim data from the saved hashMap. Also handles compatibility with old saves, see OldSaveHandler */ private static void loadInternal() - { - // SenseiKiwi: This is a temporary function for testing purposes. - // We'll move on to using a text-based format in the future. - + { if (!DimensionManager.getWorld(OVERWORLD_DIMENSION_ID).isRemote && DimensionManager.getCurrentSaveRootDirectory() != null) { - System.out.println("Loading Dimensional Doors save data..."); - /*File saveFile = new File(DimensionManager.getCurrentSaveRootDirectory() + "/dimdoors.dat"); + // Load and register blacklisted dimension IDs - setState(saveData);*/ - System.out.println("Loaded successfully!"); + // Load save data + System.out.println("Loading Dimensional Doors save data..."); + if (DDSaveHandler.loadAll()) + { + System.out.println("Loaded successfully!"); + } } } public static void save() { - // SenseiKiwi: This is a temporary function for testing purposes. - // We'll move on to using a text-based format in the future. - if (!isLoaded) { return; } World world = DimensionManager.getWorld(OVERWORLD_DIMENSION_ID); - if (world == null || world.isRemote || DimensionManager.getCurrentSaveRootDirectory() != null) + if (world == null || world.isRemote || DimensionManager.getCurrentSaveRootDirectory() == null) { return; } @@ -256,30 +299,23 @@ public class PocketManager { return; } - isSaving = true; + try { System.out.println("Writing Dimensional Doors save data..."); - /*String tempPath = DimensionManager.getCurrentSaveRootDirectory() + "/dimdoors.tmp"; - String savePath = DimensionManager.getCurrentSaveRootDirectory() + "/dimdoors.dat"; - File tempFile = new File(tempPath); - File saveFile = new File(savePath); - DataOutputStream writer = new DataOutputStream(new FileOutputStream(tempFile)); - getState().writeToStream(writer); - writer.close(); - saveFile.delete(); - tempFile.renameTo(saveFile);*/ - System.out.println("Saved successfully!"); + if ( DDSaveHandler.saveAll(dimensionData.values()) ) + { + System.out.println("Saved successfully!"); + } } - /*catch (FileNotFoundException e) + catch (Exception e) { - e.printStackTrace(); + // Wrap the exception in a RuntimeException so functions that call + // PocketManager.save() don't need to catch it. We want MC to + // crash if something really bad happens rather than ignoring it! + throw new RuntimeException(e); } - catch (IOException e) - { - e.printStackTrace(); - }*/ finally { isSaving = false; diff --git a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java index cebfefb..42cac91 100644 --- a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java +++ b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java @@ -33,29 +33,11 @@ import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPackConfigReader; import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonType; import StevenDimDoors.mod_pocketDim.items.ItemDimensionalDoor; import StevenDimDoors.mod_pocketDim.util.ConfigurationProcessingException; +import StevenDimDoors.mod_pocketDim.util.FileFilters; import StevenDimDoors.mod_pocketDim.util.WeightedContainer; public class DungeonHelper { - //TODO: File-handling functionality should be spun off to a helper class later - private static class DirectoryFilter implements FileFilter - { - @Override - public boolean accept(File file) - { - return file.isDirectory(); - } - } - - private static class SchematicFileFilter implements FileFilter - { - @Override - public boolean accept(File file) - { - return file.isFile() && file.getName().endsWith(SCHEMATIC_FILE_EXTENSION); - } - } - private static DungeonHelper instance = null; private static DDProperties properties = null; @@ -387,7 +369,7 @@ public class DungeonHelper File[] packFiles; ArrayList packFilePaths; File directory = new File(path); - SchematicFileFilter schematicFileFilter = new SchematicFileFilter(); + FileFilter schematicFileFilter = new FileFilters.FileExtensionFilter(SCHEMATIC_FILE_EXTENSION); //Check that the Ruins pack has been loaded if (RuinsPack == null) @@ -411,7 +393,7 @@ public class DungeonHelper schematics = null; //Release memory //Load the custom dungeon packs - packDirectories = directory.listFiles(new DirectoryFilter()); + packDirectories = directory.listFiles(new FileFilters.DirectoryFilter()); if (packDirectories != null) { //Loop through each directory, which is assumed to be a dungeon pack diff --git a/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java b/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java new file mode 100644 index 0000000..fe63994 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java @@ -0,0 +1,136 @@ +package StevenDimDoors.mod_pocketDim.saving; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import net.minecraftforge.common.DimensionManager; +import StevenDimDoors.mod_pocketDim.util.FileFilters; + +import com.google.common.io.Files; + +public class DDSaveHandler +{ + public static boolean loadAll() + { + // SenseiKiwi: Loading up our save data is not as simple as just reading files. + // To properly restore dimensions, we need to make sure we always load + // a dimension's parent and root before trying to load it. We'll use + // topological sorting to determine the order in which to recreate the + // dimension objects such that we respect those dependencies. + // Links must be loaded after instantiating all the dimensions and must + // be checked against our dimension blacklist. + + // Don't surround this code with try-catch. Our mod should crash if an error + // occurs at this level, since it could lead to some nasty problems. + + String basePath = DimensionManager.getCurrentSaveRootDirectory() + "/DimensionalDoors/data/"; + File dataDirectory = new File(basePath); + + // Check if the folder exists. If it doesn't, just return. + if (!dataDirectory.exists()) + { + return true; + } + + // Load the dimension blacklist + // --insert code here-- + + // List any dimension data files and read each dimension + DimDataProcessor reader = new DimDataProcessor(); + List packedDims = new ArrayList(); + FileFilter dataFileFilter = new FileFilters.RegexFileFilter("dim_-?\\d+\\.txt"); + + File[] dataFiles = dataDirectory.listFiles(dataFileFilter); + for (File dataFile : dataFiles) + { + PackedDimData packedDim = readDimension(dataFile, reader); + } + return true; + } + + private static PackedDimData readDimension(File dataFile, DimDataProcessor reader) + { + try + { + return reader.readFromFile(dataFile); + } + catch (Exception e) + { + System.err.println("Could not read dimension data from: " + dataFile.getAbsolutePath()); + System.err.println("The following error occurred:"); + printException(e, false); + return null; + } + } + + public static boolean saveAll(Iterable> dimensions) throws IOException + { + // Create the data directory for our dimensions + // Don't catch exceptions here. If we can't create this folder, + // the mod should crash to let the user know early on. + + String basePath = DimensionManager.getCurrentSaveRootDirectory() + "/DimensionalDoors/data/"; + File basePathFile = new File(basePath); + Files.createParentDirs(basePathFile); + basePathFile = null; + basePath += "dim_"; + + boolean succeeded = true; + DimDataProcessor writer = new DimDataProcessor(); + for (IPackable dimension : dimensions) + { + succeeded &= writeDimension(dimension, writer, basePath); + } + return succeeded; + } + + private static boolean writeDimension(IPackable dimension, DimDataProcessor writer, String basePath) + { + try + { + File tempFile = new File(basePath + (dimension.name() + ".tmp")); + File saveFile = new File(basePath + (dimension.name() + ".txt")); + writer.writeToFile(tempFile, dimension.pack()); + saveFile.delete(); + tempFile.renameTo(saveFile); + return true; + } + catch (Exception e) + { + System.err.println("Could not save data for dimension #" + dimension.name() + ". The following error occurred:"); + printException(e, false); + return false; + } + } + + private static void printException(Exception e, boolean verbose) + { + if (e.getCause() == null) + { + if (verbose) + { + e.printStackTrace(); + } + else + { + System.err.println(e.getMessage()); + } + } + else + { + System.out.println(e.getMessage()); + System.err.println("Caused by an underlying error:"); + if (verbose) + { + e.getCause().printStackTrace(); + } + else + { + System.err.println(e.getCause().getMessage()); + } + } + } +} diff --git a/StevenDimDoors/mod_pocketDim/saving/DimDataProcessor.java b/StevenDimDoors/mod_pocketDim/saving/DimDataProcessor.java new file mode 100644 index 0000000..6102182 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/saving/DimDataProcessor.java @@ -0,0 +1,28 @@ +package StevenDimDoors.mod_pocketDim.saving; + +import java.io.InputStream; +import java.io.OutputStream; + +import StevenDimDoors.mod_pocketDim.util.BaseConfigurationProcessor; +import StevenDimDoors.mod_pocketDim.util.ConfigurationProcessingException; + +public class DimDataProcessor extends BaseConfigurationProcessor +{ + + @Override + public PackedDimData readFromStream(InputStream inputStream) + throws ConfigurationProcessingException + { + // TODO Auto-generated method stub + return null; + } + + @Override + public void writeToStream(OutputStream outputStream, PackedDimData data) + throws ConfigurationProcessingException + { + // TODO Auto-generated method stub + + } + +} diff --git a/StevenDimDoors/mod_pocketDim/saving/IPackable.java b/StevenDimDoors/mod_pocketDim/saving/IPackable.java new file mode 100644 index 0000000..78908d8 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/saving/IPackable.java @@ -0,0 +1,7 @@ +package StevenDimDoors.mod_pocketDim.saving; + +public interface IPackable +{ + public String name(); + public T pack(); +} diff --git a/StevenDimDoors/mod_pocketDim/saving/PackedDimData.java b/StevenDimDoors/mod_pocketDim/saving/PackedDimData.java new file mode 100644 index 0000000..38af8f1 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/saving/PackedDimData.java @@ -0,0 +1,42 @@ +package StevenDimDoors.mod_pocketDim.saving; + +import java.util.List; + +import StevenDimDoors.mod_pocketDim.Point3D; + +public class PackedDimData +{ + // These fields will be public since this is a simple data container + public final int ID; + public final boolean IsDungeon; + public final boolean IsFilled; + public final int Depth; + public final int PackDepth; + public final int ParentID; + public final int RootID; + public final Point3D Origin; + public final int Orientation; + public final List ChildIDs; + public final List Links; + public final List Tails; + + // FIXME Missing dungeon data, not sure how to include it + + public PackedDimData(int id, int depth, int packDepth, int parentID, int rootID, int orientation, + boolean isDungeon, boolean isFilled, Point3D origin, List childIDs, List links, + List tails) + { + ID = id; + Depth = depth; + PackDepth = packDepth; + ParentID = parentID; + RootID = rootID; + Orientation = orientation; + IsDungeon = isDungeon; + IsFilled = isFilled; + Origin = origin; + ChildIDs = childIDs; + Links = links; + Tails = tails; + } +} diff --git a/StevenDimDoors/mod_pocketDim/saving/PackedLinkData.java b/StevenDimDoors/mod_pocketDim/saving/PackedLinkData.java new file mode 100644 index 0000000..f82e74a --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/saving/PackedLinkData.java @@ -0,0 +1,6 @@ +package StevenDimDoors.mod_pocketDim.saving; + +public class PackedLinkData +{ + +} diff --git a/StevenDimDoors/mod_pocketDim/saving/PackedLinkTail.java b/StevenDimDoors/mod_pocketDim/saving/PackedLinkTail.java new file mode 100644 index 0000000..c5a1bb7 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/saving/PackedLinkTail.java @@ -0,0 +1,6 @@ +package StevenDimDoors.mod_pocketDim.saving; + +public class PackedLinkTail +{ + +} diff --git a/StevenDimDoors/mod_pocketDim/util/FileFilters.java b/StevenDimDoors/mod_pocketDim/util/FileFilters.java new file mode 100644 index 0000000..efd4e3b --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/util/FileFilters.java @@ -0,0 +1,51 @@ +package StevenDimDoors.mod_pocketDim.util; + +import java.io.File; +import java.io.FileFilter; +import java.util.regex.Pattern; + +public class FileFilters +{ + private FileFilters() { } + + public static class DirectoryFilter implements FileFilter + { + @Override + public boolean accept(File file) + { + return file.isDirectory(); + } + } + + public static class FileExtensionFilter implements FileFilter + { + private final String extension; + + public FileExtensionFilter(String extension) + { + this.extension = extension; + } + + @Override + public boolean accept(File file) + { + return file.isFile() && file.getName().endsWith(extension); + } + } + + public static class RegexFileFilter implements FileFilter + { + private final Pattern pattern; + + public RegexFileFilter(String expression) + { + this.pattern = Pattern.compile(expression); + } + + @Override + public boolean accept(File file) + { + return file.isFile() && pattern.matcher(file.getName()).matches(); + } + } +}