Add system for dimensions to be marked for deletion (#6515)

This commit is contained in:
Take Weiland authored and GitHub committed 2020-06-15 09:37:08 -07:00
1 parent 9114bec81d
commit 6345f2670c
4 files changed
+290 -6

No files matched your search

@@ -19,6 +19,7 @@
package net.minecraftforge.common;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -27,6 +28,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
@@ -44,7 +47,9 @@ import com.google.common.collect.Multiset;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.StringNBT;
import net.minecraft.network.PacketBuffer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.management.PlayerList;
@@ -58,6 +63,8 @@ import net.minecraft.world.chunk.listener.IChunkStatusListener;
import net.minecraft.world.server.ServerMultiWorld;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.storage.SaveHandler;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.event.world.RegisterDimensionsEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.StartupQuery;
@@ -65,6 +72,7 @@ import net.minecraftforge.fml.server.ServerModLoader;
import net.minecraftforge.registries.ClearableRegistry;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -85,6 +93,7 @@ public class DimensionManager
private static final ConcurrentMap<World, World> weakWorldMap = new MapMaker().weakKeys().weakValues().makeMap();
private static final Multiset<Integer> leakedWorlds = HashMultiset.create();
private static final Map<ResourceLocation, SavedEntry> savedEntries = new HashMap<>();
private static final List<String> foldersScheduledForDeletion = new ArrayList<>();
private static volatile Set<World> playerWorlds = new HashSet<>();
/**
@@ -100,7 +109,8 @@ public class DimensionManager
* @param magnifier The biome generation processor
* @return the DimensionType for the dimension.
*/
public static DimensionType registerOrGetDimension(ResourceLocation name, ModDimension type, PacketBuffer data, boolean hasSkyLight) {
public static DimensionType registerOrGetDimension(ResourceLocation name, ModDimension type, PacketBuffer data, boolean hasSkyLight)
{
return REGISTRY.getValue(name).orElseGet(()->registerDimension(name, type, data, hasSkyLight));
}
/**
@@ -199,14 +209,49 @@ public class DimensionManager
return ret;
}
/**
* Marks the given dimension for deletion upon next reload of the save.
* The corresponding data directory will be deleted as well.
*
* Note that the dimension need not be currently unloaded. The dimension will stay functioning as normal
* until the save is reloaded.
*
* Vanilla dimensions are not supported and will throw an exception.
*
* @param dim the dimension to delete
*/
public static void markForDeletion(DimensionType dim)
{
if (dim.isVanilla())
{
throw new IllegalArgumentException("Cannot delete vanilla dimensions.");
}
getData(dim).markedForDeletion = true;
Path base = Paths.get(".").toAbsolutePath().normalize();
Path directory = dim.getDirectory(base.toFile()).toPath().toAbsolutePath().normalize();
if (!directory.startsWith(base))
{
LOGGER.warn("DimensionType {} returned save directory outside of base directory. This is bad. The directory will not be deleted.", dim);
}
else
{
String relative = base.relativize(directory).toString();
foldersScheduledForDeletion.add(relative);
}
}
//==========================================================================================================
// FORGE INTERNAL
//==========================================================================================================
/**
* Deprecated, unregistering dimensions at runtime is not supported.
* @see DimensionManager#markForDeletion(DimensionType)
*/
@Deprecated
public static void unregisterDimension(int id)
{
Validate.isTrue(dimensions.containsKey(id), String.format("Failed to unregister dimension for id %d; No provider registered", id));
dimensions.remove(id);
}
public static DimensionType registerDimensionInternal(int id, ResourceLocation name, ModDimension type, PacketBuffer data, boolean hasSkyLight)
@@ -347,7 +392,7 @@ public class DimensionManager
data.putInt("version", 1);
List<SavedEntry> list = new ArrayList<>();
for (DimensionType type : REGISTRY)
list.add(new SavedEntry(type));
list.add(new SavedEntry(type, getData(type).markedForDeletion));
savedEntries.values().forEach(list::add);
Collections.sort(list, (a, b) -> a.id - b.id);
@@ -355,6 +400,10 @@ public class DimensionManager
list.forEach(e -> lst.add(e.write()));
data.put("entries", lst);
ListNBT deleteLst = new ListNBT();
foldersScheduledForDeletion.forEach(folder -> deleteLst.add(StringNBT.valueOf(folder)));
data.put("delete_dirs", deleteLst);
}
public static void readRegistry(CompoundNBT data)
@@ -396,7 +445,7 @@ public class DimensionManager
continue;
}
}
else
else if (!entry.markedForDeletion)
{
ModDimension mod = ForgeRegistries.MOD_DIMENSIONS.getValue(entry.type);
if (mod == null)
@@ -408,6 +457,46 @@ public class DimensionManager
registerDimensionInternal(entry.id, entry.name, mod, entry.data == null ? null : new PacketBuffer(Unpooled.wrappedBuffer(entry.data)), entry.skyLight());
}
}
foldersScheduledForDeletion.clear();
ListNBT folderList = data.getList("delete_dirs", Constants.NBT.TAG_STRING);
folderList.stream()
.map(INBT::getString)
.forEach(foldersScheduledForDeletion::add);
}
public static void processScheduledDeletions(SaveHandler saveHandler)
{
List<String> toDelete = new ArrayList<>(foldersScheduledForDeletion);
foldersScheduledForDeletion.clear();
if (!toDelete.isEmpty())
{
StringBuilder text = new StringBuilder();
text.append("The following dimensions are marked for deletion by their owning mod. Proceed?\n\n");
for (String folder : toDelete)
{
text.append(folder).append('\n');
}
if (!StartupQuery.confirm(text.toString()))
{
StartupQuery.abort();
return;
}
}
for (String folderName : toDelete)
{
File folder = new File(saveHandler.getWorldDirectory(), folderName);
try
{
FileUtils.deleteDirectory(folder);
LOGGER.info(DIMMGR, "Modded dimension directory {} scheduled for deletion was deleted.", folderName);
}
catch (Exception e)
{
LOGGER.error(DIMMGR, "Failed to delete modded dimension directory {} that was scheduled for deletion.", folderName, e);
}
}
}
public static void fireRegister()
@@ -436,6 +525,7 @@ public class DimensionManager
{
int ticksWaited = 0;
boolean keepLoaded = false;
boolean markedForDeletion = false;
}
public static class SavedEntry
@@ -445,6 +535,7 @@ public class DimensionManager
ResourceLocation type;
byte[] data;
boolean skyLight;
boolean markedForDeletion;
public int getId()
{
@@ -480,9 +571,10 @@ public class DimensionManager
this.type = data.contains("type", 8) ? new ResourceLocation(data.getString("type")) : null;
this.data = data.contains("data", 7) ? data.getByteArray("data") : null;
this.skyLight = data.contains("sky_light", 99) ? data.getBoolean("sky_light") : true;
this.markedForDeletion = data.getBoolean("marked_for_deletion");
}
private SavedEntry(DimensionType data)
private SavedEntry(DimensionType data, boolean markedForDeletion)
{
this.id = REGISTRY.getId(data);
this.name = REGISTRY.getKey(data);
@@ -491,6 +583,7 @@ public class DimensionManager
if (data.getData() != null)
this.data = data.getData().array();
this.skyLight = data.hasSkyLight();
this.markedForDeletion = markedForDeletion;
}
private CompoundNBT write()
@@ -503,6 +596,7 @@ public class DimensionManager
if (data != null)
ret.putByteArray("data", data);
ret.putBoolean("sky_light", skyLight);
ret.putBoolean("marked_for_deletion", markedForDeletion);
return ret;
}
}
@@ -158,6 +158,7 @@ public class ForgeMod implements WorldPersistenceHooks.WorldPersistenceHook
{
if (tag.contains("dims", 10))
DimensionManager.readRegistry(tag.getCompound("dims"));
DimensionManager.processScheduledDeletions(handler);
}
public void mappingChanged(FMLModIdMappingEvent evt)