From 6fd9a2c00e8f938bbc7b2b7d28c8e8b8b310fb22 Mon Sep 17 00:00:00 2001 From: LexManos Date: Mon, 21 Mar 2016 17:12:34 -0700 Subject: [PATCH] Rework DimensionManager for new DimensionType enum, replaces the old provider registry. Also fixed save folder issues with dimensions. Closes #2570 --- .../minecraft/world/DimensionType.java.patch | 37 ++++++++ .../world/WorldProviderSurface.java.patch | 2 +- .../minecraft/world/WorldServer.java.patch | 3 +- .../world/gen/ChunkProviderServer.java.patch | 2 +- .../common/DimensionManager.java | 75 ++++------------ .../common/WorldSpecificSaveHandler.java | 35 +++++--- .../network/DimensionMessageHandler.java | 3 +- .../common/network/ForgeMessage.java | 13 ++- .../common/util/EnumHelper.java | 88 +++++++++++++++++-- 9 files changed, 174 insertions(+), 84 deletions(-) create mode 100644 patches/minecraft/net/minecraft/world/DimensionType.java.patch diff --git a/patches/minecraft/net/minecraft/world/DimensionType.java.patch b/patches/minecraft/net/minecraft/world/DimensionType.java.patch new file mode 100644 index 0000000000..b7c257354a --- /dev/null +++ b/patches/minecraft/net/minecraft/world/DimensionType.java.patch @@ -0,0 +1,37 @@ +--- ../src-base/minecraft/net/minecraft/world/DimensionType.java ++++ ../src-work/minecraft/net/minecraft/world/DimensionType.java +@@ -13,6 +13,7 @@ + private final String field_186075_e; + private final String field_186076_f; + private final Class field_186077_g; ++ private boolean shouldLoadSpawn = false; + + private DimensionType(int p_i46672_3_, String p_i46672_4_, String p_i46672_5_, Class p_i46672_6_) + { +@@ -20,6 +21,7 @@ + this.field_186075_e = p_i46672_4_; + this.field_186076_f = p_i46672_5_; + this.field_186077_g = p_i46672_6_; ++ this.shouldLoadSpawn = p_i46672_3_ == 0; + } + + public int func_186068_a() +@@ -74,4 +76,18 @@ + + throw new IllegalArgumentException("Invalid dimension id " + p_186069_0_); + } ++ ++ public boolean shouldLoadSpawn(){ return this.shouldLoadSpawn; } ++ public DimensionType setLoadSpawn(boolean value) { this.shouldLoadSpawn = value; return this; } ++ ++ private static Class[] ENUM_ARGS = {int.class, String.class, String.class, Class.class}; ++ static { net.minecraftforge.common.util.EnumHelper.testEnum(DimensionType.class, ENUM_ARGS); } ++ public static DimensionType register(String name, String suffix, int id, Class provider, boolean keepLoaded) ++ { ++ String enum_name = name.replace(" ", "_").toLowerCase(); ++ DimensionType ret = net.minecraftforge.common.util.EnumHelper.addEnum(DimensionType.class, enum_name, ENUM_ARGS, ++ id, name, suffix, provider); ++ return ret.setLoadSpawn(keepLoaded); ++ } ++ //TODO: Unregister? There is no way to really delete a enum value... + } diff --git a/patches/minecraft/net/minecraft/world/WorldProviderSurface.java.patch b/patches/minecraft/net/minecraft/world/WorldProviderSurface.java.patch index 97526ec7e0..1b588e11e8 100644 --- a/patches/minecraft/net/minecraft/world/WorldProviderSurface.java.patch +++ b/patches/minecraft/net/minecraft/world/WorldProviderSurface.java.patch @@ -5,6 +5,6 @@ public boolean func_186056_c(int p_186056_1_, int p_186056_2_) { - return !this.field_76579_a.func_72916_c(p_186056_1_, p_186056_2_); -+ return !this.field_76579_a.func_72916_c(p_186056_1_, p_186056_2_) || !net.minecraftforge.common.DimensionManager.shouldLoadSpawn(this.field_76579_a.field_73011_w.getDimension()); ++ return !this.field_76579_a.func_72916_c(p_186056_1_, p_186056_2_) || !this.field_76579_a.field_73011_w.func_186058_p().shouldLoadSpawn(); } } diff --git a/patches/minecraft/net/minecraft/world/WorldServer.java.patch b/patches/minecraft/net/minecraft/world/WorldServer.java.patch index e18196b2ec..26182ee574 100644 --- a/patches/minecraft/net/minecraft/world/WorldServer.java.patch +++ b/patches/minecraft/net/minecraft/world/WorldServer.java.patch @@ -10,7 +10,8 @@ + public WorldServer(MinecraftServer p_i45921_1_, ISaveHandler p_i45921_2_, WorldInfo p_i45921_3_, int p_i45921_4_, Profiler p_i45921_5_) { - super(p_i45921_2_, p_i45921_3_, DimensionType.func_186069_a(p_i45921_4_).func_186070_d(), p_i45921_5_, false); +- super(p_i45921_2_, p_i45921_3_, DimensionType.func_186069_a(p_i45921_4_).func_186070_d(), p_i45921_5_, false); ++ super(p_i45921_2_, p_i45921_3_, net.minecraftforge.common.DimensionManager.createProviderFor(p_i45921_4_), p_i45921_5_, false); this.field_73061_a = p_i45921_1_; this.field_73062_L = new EntityTracker(this); this.field_73063_M = new PlayerManager(this); diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch index 0b75b82690..6234904639 100644 --- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch +++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch @@ -100,7 +100,7 @@ this.field_73244_f.func_76159_d(olong.longValue()); this.field_73245_g.remove(chunk); + net.minecraftforge.common.ForgeChunkManager.putDormantChunk(ChunkCoordIntPair.func_77272_a(chunk.field_76635_g, chunk.field_76647_h), chunk); -+ if(field_73245_g.size() == 0 && net.minecraftforge.common.ForgeChunkManager.getPersistentChunksFor(this.field_73251_h).size() == 0 && !net.minecraftforge.common.DimensionManager.shouldLoadSpawn(this.field_73251_h.field_73011_w.getDimension())){ ++ if(field_73245_g.size() == 0 && net.minecraftforge.common.ForgeChunkManager.getPersistentChunksFor(this.field_73251_h).size() == 0 && !this.field_73251_h.field_73011_w.func_186058_p().shouldLoadSpawn()){ + net.minecraftforge.common.DimensionManager.unloadWorld(this.field_73251_h.field_73011_w.getDimension()); + return false; + } diff --git a/src/main/java/net/minecraftforge/common/DimensionManager.java b/src/main/java/net/minecraftforge/common/DimensionManager.java index 4cfb66638c..b329436cb0 100644 --- a/src/main/java/net/minecraftforge/common/DimensionManager.java +++ b/src/main/java/net/minecraftforge/common/DimensionManager.java @@ -20,13 +20,11 @@ import com.google.common.collect.Multiset; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.server.MinecraftServer; +import net.minecraft.world.DimensionType; import net.minecraft.world.MinecraftException; import net.minecraft.world.World; import net.minecraft.world.WorldManager; import net.minecraft.world.WorldProvider; -import net.minecraft.world.WorldProviderEnd; -import net.minecraft.world.WorldProviderHell; -import net.minecraft.world.WorldProviderSurface; import net.minecraft.world.WorldServer; import net.minecraft.world.WorldServerMulti; import net.minecraft.world.storage.ISaveHandler; @@ -37,51 +35,24 @@ import net.minecraftforge.fml.common.FMLLog; public class DimensionManager { - private static Hashtable> providers = new Hashtable>(); - private static Hashtable spawnSettings = new Hashtable(); private static Hashtable worlds = new Hashtable(); private static boolean hasInit = false; - private static Hashtable dimensions = new Hashtable(); + private static Hashtable dimensions = new Hashtable(); private static ArrayList unloadQueue = new ArrayList(); private static BitSet dimensionMap = new BitSet(Long.SIZE << 4); private static ConcurrentMap weakWorldMap = new MapMaker().weakKeys().weakValues().makeMap(); private static Multiset leakedWorlds = HashMultiset.create(); - public static boolean registerProviderType(int id, Class provider, boolean keepLoaded) - { - if (providers.containsKey(id)) - { - return false; - } - providers.put(id, provider); - spawnSettings.put(id, keepLoaded); - return true; - } - /** - * Unregisters a Provider type, and returns a array of all dimensions that are - * registered to this provider type. - * If the return size is greater then 0, it is required that the caller either - * change those dimensions's registered type, or replace this type before the - * world is attempted to load, else the loader will throw an exception. - * - * @param id The provider type ID to unreigster - * @return An array containing all dimension IDs still registered to this provider type. + * Returns a list of dimensions associated with this DimensionType. */ - public static int[] unregisterProviderType(int id) + public static int[] getDimensions(DimensionType type) { - if (!providers.containsKey(id)) - { - return new int[0]; - } - providers.remove(id); - spawnSettings.remove(id); - int[] ret = new int[dimensions.size()]; int x = 0; - for (Map.Entry ent : dimensions.entrySet()) + for (Map.Entry ent : dimensions.entrySet()) { - if (ent.getValue() == id) + if (ent.getValue() == type) { ret[x++] = ent.getKey(); } @@ -99,25 +70,19 @@ public class DimensionManager hasInit = true; - registerProviderType( 0, WorldProviderSurface.class, true); - registerProviderType(-1, WorldProviderHell.class, true); - registerProviderType( 1, WorldProviderEnd.class, false); - registerDimension( 0, 0); - registerDimension(-1, -1); - registerDimension( 1, 1); + registerDimension( 0, DimensionType.OVERWORLD); + registerDimension(-1, DimensionType.NETHER); + registerDimension( 1, DimensionType.THE_END); } - public static void registerDimension(int id, int providerType) + public static void registerDimension(int id, DimensionType type) { - if (!providers.containsKey(providerType)) - { - throw new IllegalArgumentException(String.format("Failed to register dimension for id %d, provider type %d does not exist", id, providerType)); - } + DimensionType.getById(type.getId()); //Check if type is invalid {will throw an error} No clue how it would be invalid tho... if (dimensions.containsKey(id)) { throw new IllegalArgumentException(String.format("Failed to register dimension for id %d, One is already registered", id)); } - dimensions.put(id, providerType); + dimensions.put(id, type); if (id >= 0) { dimensionMap.set(id); @@ -141,7 +106,7 @@ public class DimensionManager return dimensions.containsKey(dim); } - public static int getProviderType(int dim) + public static DimensionType getProviderType(int dim) { if (!dimensions.containsKey(dim)) { @@ -264,12 +229,6 @@ public class DimensionManager return worlds.values().toArray(new WorldServer[worlds.size()]); } - public static boolean shouldLoadSpawn(int dim) - { - int id = getProviderType(dim); - return spawnSettings.containsKey(id) && spawnSettings.get(id); - } - static { init(); @@ -289,9 +248,9 @@ public class DimensionManager { if (dimensions.containsKey(dim)) { - WorldProvider provider = providers.get(getProviderType(dim)).newInstance(); - provider.setDimension(dim); - return provider; + WorldProvider ret = getProviderType(dim).createDimension(); + ret.setDimension(dim); + return ret; } else { @@ -301,7 +260,7 @@ public class DimensionManager catch (Exception e) { FMLCommonHandler.instance().getFMLLogger().log(Level.ERROR, String.format("An error occured trying to create an instance of WorldProvider %d (%s)", - dim, providers.get(getProviderType(dim)).getSimpleName()),e); + dim, getProviderType(dim)),e); throw new RuntimeException(e); } } diff --git a/src/main/java/net/minecraftforge/common/WorldSpecificSaveHandler.java b/src/main/java/net/minecraftforge/common/WorldSpecificSaveHandler.java index 579853bec6..c2249355ad 100644 --- a/src/main/java/net/minecraftforge/common/WorldSpecificSaveHandler.java +++ b/src/main/java/net/minecraftforge/common/WorldSpecificSaveHandler.java @@ -50,24 +50,37 @@ public class WorldSpecificSaveHandler implements ISaveHandler dataDir.mkdirs(); } File file = new File(dataDir, name + ".dat"); - if (!file.exists() && name.equalsIgnoreCase("FORTRESS") && world.provider.getDimension() == -1) //Only copy over the fortress.dat for the vanilla nether. + if (!file.exists()) { - File parentFile = parent.getMapFileFromName(name); - if (parentFile.exists()) + switch (world.provider.getDimension()) { - try - { - Files.copy(parentFile, file); - } - catch (IOException e) - { - FMLLog.log(Level.ERROR, e, "A critical error occured copying fortress.dat to world specific dat folder - new file will be created."); - } + case -1: + if (name.equalsIgnoreCase("FORTRESS")) copyFile(name, file); + break; + case 1: + if (name.equalsIgnoreCase("VILLAGES_END")) copyFile(name, file); + break; } } return file; } + private void copyFile(String name, File to) + { + File parentFile = parent.getMapFileFromName(name); + if (parentFile.exists()) + { + try + { + Files.copy(parentFile, to); + } + catch (IOException e) + { + FMLLog.log(Level.ERROR, e, "A critical error occured copying %s to world specific dat folder - new file will be created.", parentFile.getName()); + } + } + } + @Override public TemplateManager getStructureTemplateManager() { diff --git a/src/main/java/net/minecraftforge/common/network/DimensionMessageHandler.java b/src/main/java/net/minecraftforge/common/network/DimensionMessageHandler.java index 010f6ee9d6..232d39a558 100644 --- a/src/main/java/net/minecraftforge/common/network/DimensionMessageHandler.java +++ b/src/main/java/net/minecraftforge/common/network/DimensionMessageHandler.java @@ -2,6 +2,7 @@ package net.minecraftforge.common.network; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import net.minecraft.world.DimensionType; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.network.ForgeMessage.DimensionRegisterMessage; import org.apache.logging.log4j.Level; @@ -13,7 +14,7 @@ public class DimensionMessageHandler extends SimpleChannelInboundHandler> T addEnum(Class enumType, String enumName, Class[] paramTypes, Object[] paramValues) + //Tests an enum is compatible with these args, throws an error if not. + public static void testEnum(Class> enumType, Class[] paramTypes) + { + addEnum(true, enumType, null, paramTypes, (Object[])null); + } + + public static > T addEnum(Class enumType, String enumName, Class[] paramTypes, Object... paramValues) + { + return addEnum(false, enumType, enumName, paramTypes, paramValues); + } + + @SuppressWarnings({ "unchecked", "serial" }) + private static > T addEnum(boolean test, final Class enumType, String enumName, final Class[] paramTypes, Object[] paramValues) { if (!isSetup) { @@ -243,14 +257,74 @@ public class EnumHelper if (valuesField == null) { - FMLLog.severe("Could not find $VALUES field for enum: %s", enumType.getName()); - FMLLog.severe("Runtime Deobf: %s", FMLForgePlugin.RUNTIME_DEOBF); - FMLLog.severe("Flags: %s", String.format("%16s", Integer.toBinaryString(flags)).replace(' ', '0')); - FMLLog.severe("Fields:"); + final List lines = Lists.newArrayList(); + lines.add(String.format("Could not find $VALUES field for enum: %s", enumType.getName())); + lines.add(String.format("Runtime Deobf: %s", FMLForgePlugin.RUNTIME_DEOBF)); + lines.add(String.format("Flags: %s", String.format("%16s", Integer.toBinaryString(flags)).replace(' ', '0'))); + lines.add( "Fields:"); for (Field field : fields) { String mods = String.format("%16s", Integer.toBinaryString(field.getModifiers())).replace(' ', '0'); - FMLLog.severe(" %s %s: %s", mods, field.getName(), field.getType().getName()); + lines.add(String.format(" %s %s: %s", mods, field.getName(), field.getType().getName())); + } + + for (String line : lines) + FMLLog.severe(line); + + if (test) + { + throw new EnhancedRuntimeException("Could not find $VALUES field for enum: " + enumType.getName()) + { + @Override + protected void printStackTrace(WrappedPrintStream stream) + { + for (String line : lines) + stream.println(line); + } + }; + } + return null; + } + + if (test) + { + Object ctr = null; + Exception ex = null; + try + { + ctr = getConstructorAccessor(enumType, paramTypes); + } + catch (Exception e) + { + ex = e; + } + if (ctr == null || ex != null) + { + throw new EnhancedRuntimeException(String.format("Could not find constructor for Enum %s", enumType.getName()), ex) + { + private String toString(Class[] cls) + { + StringBuilder b = new StringBuilder(); + for (int x = 0; x < cls.length; x++) + { + b.append(cls[x].getName()); + if (x != cls.length - 1) + b.append(", "); + } + return b.toString(); + } + @Override + protected void printStackTrace(WrappedPrintStream stream) + { + stream.println("Target Arguments:"); + stream.println(" java.lang.String, int, " + toString(paramTypes)); + stream.println("Found Constructors:"); + for (Constructor ctr : enumType.getDeclaredConstructors()) + { + stream.println(" " + toString(ctr.getParameterTypes())); + } + } + }; } return null; }