From 96711794536a42626a504635f3a4bd1fb82f72ed Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 11 Apr 2015 17:18:53 -0400 Subject: [PATCH] Fluids are now tracked internally by mod. This allows for the server and the world to specify a "default" in the case of a possible alternative fluid implementation. If you always called registerFluid, things should work pretty seamlessly, but if you didn't (gating with an isFluidRegistered check for example) you should change to register anyway. This way, even if you're not default in the overall instance, you may become default if you're the only mod present on a server, for example, or in a world save. This should radically decrease the mixups caused by mod load ordering problems, and other issues around fluid tracking. --- .../common/ForgeModContainer.java | 3 + .../FluidIdRegistryMessageHandler.java | 2 +- .../common/network/ForgeMessage.java | 28 ++++ .../minecraftforge/fluids/FluidRegistry.java | 120 +++++++++++++++++- .../net/minecraftforge/fluids/FluidStack.java | 4 - 5 files changed, 145 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/minecraftforge/common/ForgeModContainer.java b/src/main/java/net/minecraftforge/common/ForgeModContainer.java index fe9459c1c..366ad2be7 100644 --- a/src/main/java/net/minecraftforge/common/ForgeModContainer.java +++ b/src/main/java/net/minecraftforge/common/ForgeModContainer.java @@ -26,6 +26,7 @@ import net.minecraftforge.classloading.FMLForgePlugin; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; import net.minecraftforge.common.network.ForgeNetworkHandler; +import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.oredict.RecipeSorter; import net.minecraftforge.server.command.ForgeCommand; @@ -309,6 +310,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC NBTTagCompound forgeData = new NBTTagCompound(); NBTTagCompound dimData = DimensionManager.saveDimensionDataMap(); forgeData.setTag("DimensionData", dimData); + FluidRegistry.writeDefaultFluidList(forgeData); return forgeData; } @@ -316,6 +318,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC public void readData(SaveHandler handler, WorldInfo info, Map propertyMap, NBTTagCompound tag) { DimensionManager.loadDimensionDataMap(tag.hasKey("DimensionData") ? tag.getCompoundTag("DimensionData") : null); + FluidRegistry.loadFluidDefaults(tag); } @Subscribe diff --git a/src/main/java/net/minecraftforge/common/network/FluidIdRegistryMessageHandler.java b/src/main/java/net/minecraftforge/common/network/FluidIdRegistryMessageHandler.java index 032ff758e..dbb39244d 100644 --- a/src/main/java/net/minecraftforge/common/network/FluidIdRegistryMessageHandler.java +++ b/src/main/java/net/minecraftforge/common/network/FluidIdRegistryMessageHandler.java @@ -12,7 +12,7 @@ public class FluidIdRegistryMessageHandler extends SimpleChannelInboundHandler fluidIds = HashBiMap.create(); + Set defaultFluids = Sets.newHashSet(); @Override void toBytes(ByteBuf bytes) { @@ -49,6 +58,11 @@ public abstract class ForgeMessage { ByteBufUtils.writeUTF8String(bytes,entry.getKey().getName()); bytes.writeInt(entry.getValue()); } + for (Map.Entry entry : ids.entrySet()) + { + String defaultName = FluidRegistry.getDefaultFluidName(entry.getKey()); + ByteBufUtils.writeUTF8String(bytes, defaultName); + } } @Override @@ -60,6 +74,20 @@ public abstract class ForgeMessage { int fluidId = bytes.readInt(); fluidIds.put(FluidRegistry.getFluid(fluidName), fluidId); } + // do we have a defaults list? + + if (bytes.isReadable()) + { + for (int i = 0; i < listSize; i++) + { + defaultFluids.add(ByteBufUtils.readUTF8String(bytes)); + } + } + else + { + FMLLog.getLogger().log(Level.INFO, "Legacy server message contains no default fluid list - there may be problems with fluids"); + defaultFluids.clear(); + } } } diff --git a/src/main/java/net/minecraftforge/fluids/FluidRegistry.java b/src/main/java/net/minecraftforge/fluids/FluidRegistry.java index 83f8565ae..cb25e471b 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidRegistry.java +++ b/src/main/java/net/minecraftforge/fluids/FluidRegistry.java @@ -1,21 +1,29 @@ package net.minecraftforge.fluids; -import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; + +import org.apache.logging.log4j.Level; import net.minecraft.block.Block; import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; import net.minecraft.util.StatCollector; import net.minecraftforge.common.MinecraftForge; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.eventhandler.Event; +import cpw.mods.fml.common.network.ByteBufUtils; /** * Handles Fluid registrations. Fluids MUST be registered in order to function. @@ -32,6 +40,10 @@ public abstract class FluidRegistry static BiMap fluidNames = HashBiMap.create(); //Caching this just makes some other calls faster static BiMap fluidBlocks; + // the globally unique fluid map - only used to associate non-defaults during world/server loading + static BiMap masterFluidReference = HashBiMap.create(); + static BiMap defaultFluidName = HashBiMap.create(); + public static final Fluid WATER = new Fluid("water") { @Override public String getLocalizedName() { @@ -60,39 +72,97 @@ public abstract class FluidRegistry * Called by Forge to prepare the ID map for server -> client sync. * Modders, DO NOT call this. */ - public static void initFluidIDs(BiMap newfluidIDs) + public static void initFluidIDs(BiMap newfluidIDs, Set defaultNames) { maxID = newfluidIDs.size(); fluidIDs.clear(); fluidIDs.putAll(newfluidIDs); fluidNames.clear(); - for (Entry e : fluidIDs.entrySet()) + for (Entry e : fluidIDs.entrySet()) { fluidNames.put(e.getValue(), e.getKey().getName()); + } + loadFluidDefaults(defaultNames); } /** - * Register a new Fluid. If a fluid with the same name already exists, registration is denied. + * Called by forge to load default fluid IDs from the world or from server -> client for syncing + * DO NOT call this and expect useful behaviour. + */ + private static void loadFluidDefaults(Set defaultNames) + { + // If there's an empty set of default names, use the defaults as defined locally + if (defaultNames.isEmpty()) { + defaultNames.addAll(defaultFluidName.values()); + } + for (String defaultName : defaultNames) + { + Fluid fluid = masterFluidReference.get(defaultName); + if (fluid == null) { + String derivedName = defaultName.split(":",2)[1]; + String localDefault = defaultFluidName.get(derivedName); + if (localDefault == null) { + FMLLog.getLogger().log(Level.ERROR, "The fluid {} (specified as {}) is missing from this instance - it will be removed", derivedName, defaultName); + continue; + } + fluid = masterFluidReference.get(localDefault); + FMLLog.getLogger().log(Level.ERROR, "The fluid {} specified as default is not present - it will be reverted to default {}", defaultName, localDefault); + } + FMLLog.getLogger().log(Level.DEBUG, "The fluid {} has been selected as the default fluid for {}", defaultName, fluid.getName()); + fluids.put(fluid.getName(), fluid); + Integer id = fluidIDs.remove(fluid); + fluidIDs.put(fluid, id); + } + fluidBlocks = null; + } + + /** + * Register a new Fluid. If a fluid with the same name already exists, registration the alternative fluid is tracked + * in case it is the default in another place * * @param fluid * The fluid to register. - * @return True if the fluid was successfully registered; false if there is a name clash. + * @return True if the fluid was registered as the current default fluid, false if it was only registered as an alternative */ public static boolean registerFluid(Fluid fluid) { + masterFluidReference.put(uniqueName(fluid), fluid); + if (fluids.containsKey(fluid.getName())) { - FMLLog.bigWarning("Duplicate registration attempt for fluid %s (type %s) has occurred. This is not a problem itself, but subsequent failed FluidStacks result if this is not handled properly", fluid.getName(), fluid.getClass().getName()); return false; } fluids.put(fluid.getName(), fluid); maxID++; fluidIDs.put(fluid, maxID); fluidNames.put(maxID, fluid.getName()); + defaultFluidName.put(fluid.getName(), uniqueName(fluid)); MinecraftForge.EVENT_BUS.post(new FluidRegisterEvent(fluid.getName(), maxID)); return true; } + private static String uniqueName(Fluid fluid) + { + ModContainer activeModContainer = Loader.instance().activeModContainer(); + String activeModContainerName = activeModContainer == null ? "minecraft" : activeModContainer.getModId(); + return activeModContainerName+":"+fluid.getName(); + } + + /** + * Is the supplied fluid the current default fluid for it's name + * @param fluid the fluid we're testing + * @return if the fluid is default + */ + public static boolean isFluidDefault(Fluid fluid) + { + return fluids.containsValue(fluid); + } + + /** + * Does the supplied fluid have an entry for it's name (whether or not the fluid itself is default) + * @param fluid the fluid we're testing + * @return if the fluid's name has a registration entry + */ public static boolean isFluidRegistered(Fluid fluid) { return fluids.containsKey(fluid.getName()); @@ -207,4 +277,40 @@ public abstract class FluidRegistry { return maxID; } + + public static String getDefaultFluidName(Fluid key) + { + return masterFluidReference.inverse().get(key); + } + + public static void loadFluidDefaults(NBTTagCompound tag) + { + Set defaults = Sets.newHashSet(); + if (tag.hasKey("DefaultFluidList",9)) + { + FMLLog.getLogger().log(Level.DEBUG, "Loading persistent fluid defaults from world"); + NBTTagList tl = tag.getTagList("DefaultFluidList", 8); + for (int i = 0; i < tl.tagCount(); i++) + { + defaults.add(tl.getStringTagAt(i)); + } + } + else + { + FMLLog.getLogger().log(Level.DEBUG, "World is missing persistent fluid defaults - using local defaults"); + } + loadFluidDefaults(defaults); + } + + public static void writeDefaultFluidList(NBTTagCompound forgeData) + { + NBTTagList tagList = new NBTTagList(); + + for (Entry def : fluids.entrySet()) + { + tagList.appendTag(new NBTTagString(getDefaultFluidName(def.getValue()))); + } + + forgeData.setTag("DefaultFluidList", tagList); + } } diff --git a/src/main/java/net/minecraftforge/fluids/FluidStack.java b/src/main/java/net/minecraftforge/fluids/FluidStack.java index b485170e9..962633149 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidStack.java +++ b/src/main/java/net/minecraftforge/fluids/FluidStack.java @@ -1,10 +1,6 @@ package net.minecraftforge.fluids; -import java.util.Locale; - -import com.google.common.base.Strings; - import cpw.mods.fml.common.FMLLog; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound;