Merge branch '1.7.10' - imports all the fluid fixups and other stuff from 1.7 into 1.8

Conflicts:
	fml/src/main/java/net/minecraftforge/fml/common/FMLCommonHandler.java
	fml/src/main/java/net/minecraftforge/fml/common/Loader.java
	patches/minecraft/net/minecraft/client/Minecraft.java.patch
	patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.java.patch
	patches/minecraft/net/minecraft/item/ItemArmor.java.patch
	patches/minecraft/net/minecraft/nbt/CompressedStreamTools.java.patch
	patches/minecraft/net/minecraft/nbt/NBTTagCompound.java.patch
	patches/minecraft/net/minecraft/nbt/NBTTagList.java.patch
	patches/minecraft/net/minecraft/world/World.java.patch
	patches/minecraft/net/minecraft/world/WorldProvider.java.patch
	src/main/java/net/minecraftforge/common/ForgeVersion.java
	src/main/java/net/minecraftforge/common/network/ForgeMessage.java
	src/main/java/net/minecraftforge/fluids/BlockFluidBase.java
	src/main/java/net/minecraftforge/fluids/FluidContainerRegistry.java
	src/main/java/net/minecraftforge/fluids/FluidRegistry.java
	src/main/java/net/minecraftforge/oredict/OreDictionary.java
This commit is contained in:
cpw 2015-06-01 16:29:34 -04:00
commit d02636213a
25 changed files with 722 additions and 135 deletions

View file

@ -66,6 +66,7 @@ import net.minecraft.network.status.INetHandlerStatusServer;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.util.IThreadListener; import net.minecraft.util.IThreadListener;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StringUtils;
import net.minecraft.world.WorldSettings; import net.minecraft.world.WorldSettings;
import net.minecraft.world.storage.SaveFormatOld; import net.minecraft.world.storage.SaveFormatOld;
import net.minecraftforge.fml.client.registry.RenderingRegistry; import net.minecraftforge.fml.client.registry.RenderingRegistry;
@ -991,9 +992,17 @@ public class FMLClientHandler implements IFMLSidedHandler
public void processWindowMessages() public void processWindowMessages()
{ {
// workaround for windows requiring messages being processed on the main thread // workaround for windows requiring messages being processed on the main thread
if(LWJGLUtil.getPlatform() == LWJGLUtil.PLATFORM_WINDOWS) if (LWJGLUtil.getPlatform() != LWJGLUtil.PLATFORM_WINDOWS) return;
{ // If we can't grab the mutex, the update call is blocked, probably in native code, just skip it and carry on
Display.processMessages(); // We'll get another go next time
} if (!SplashProgress.mutex.tryAcquire()) return;
Display.processMessages();
SplashProgress.mutex.release();
}
@Override
public String stripSpecialChars(String message)
{
return StringUtils.stripControlCodes(message);
} }
} }

View file

@ -63,6 +63,7 @@ import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.Iterator; import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -131,6 +132,7 @@ public class SplashProgress
private static int barBorderColor; private static int barBorderColor;
private static int barColor; private static int barColor;
private static int barBackgroundColor; private static int barBackgroundColor;
static final Semaphore mutex = new Semaphore(1);
private static String getString(String name, String def) private static String getString(String name, String def)
{ {
@ -352,7 +354,16 @@ public class SplashProgress
glEnd(); glEnd();
glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D);
// We use mutex to indicate safely to the main thread that we're taking the display global lock
// So the main thread can skip processing messages while we're updating.
// There are system setups where this call can pause for a while, because the GL implementation
// is trying to impose a framerate or other thing is occurring. Without the mutex, the main
// thread would delay waiting for the same global display lock
mutex.acquireUninterruptibly();
Display.update(); Display.update();
// As soon as we're done, we release the mutex. The other thread can now ping the processmessages
// call as often as it wants until we get get back here again
mutex.release();
if(pause) if(pause)
{ {
clearGL(); clearGL();

View file

@ -764,4 +764,8 @@ public class FMLCommonHandler
props.clear(); props.clear();
return null; return null;
} }
public String stripSpecialChars(String message)
{
return sidedDelegate != null ? sidedDelegate.stripSpecialChars(message) : message;
}
} }

View file

@ -64,4 +64,6 @@ public interface IFMLSidedHandler
IThreadListener getWorldThread(INetHandler net); IThreadListener getWorldThread(INetHandler net);
void processWindowMessages(); void processWindowMessages();
String stripSpecialChars(String message);
} }

View file

@ -39,6 +39,7 @@ import net.minecraftforge.fml.common.functions.ArtifactVersionNameFunction;
import net.minecraftforge.fml.common.functions.ModIdFunction; import net.minecraftforge.fml.common.functions.ModIdFunction;
import net.minecraftforge.fml.common.registry.GameData; import net.minecraftforge.fml.common.registry.GameData;
import net.minecraftforge.fml.common.registry.GameRegistry; import net.minecraftforge.fml.common.registry.GameRegistry;
import net.minecraftforge.fml.common.registry.ItemStackHolderInjector;
import net.minecraftforge.fml.common.registry.ObjectHolderRegistry; import net.minecraftforge.fml.common.registry.ObjectHolderRegistry;
import net.minecraftforge.fml.common.registry.GameRegistry.Type; import net.minecraftforge.fml.common.registry.GameRegistry.Type;
import net.minecraftforge.fml.common.toposort.ModSorter; import net.minecraftforge.fml.common.toposort.ModSorter;
@ -524,8 +525,10 @@ public class Loader
return; return;
} }
ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable()); ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable());
ItemStackHolderInjector.INSTANCE.findHolders(discoverer.getASMTable());
modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir); modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir);
ObjectHolderRegistry.INSTANCE.applyObjectHolders(); ObjectHolderRegistry.INSTANCE.applyObjectHolders();
ItemStackHolderInjector.INSTANCE.inject();
modController.transition(LoaderState.INITIALIZATION, false); modController.transition(LoaderState.INITIALIZATION, false);
progressBar.step("Initializing Minecraft Engine"); progressBar.step("Initializing Minecraft Engine");
} }
@ -614,7 +617,7 @@ public class Loader
public String getFMLVersionString() public String getFMLVersionString()
{ {
return String.format("%s.%s.%s.%s", major, minor, rev, build); return "8.0.99.99";
} }
public ClassLoader getModClassLoader() public ClassLoader getModClassLoader()
@ -708,6 +711,7 @@ public class Loader
progressBar.step("Initializing mods Phase 3"); progressBar.step("Initializing mods Phase 3");
modController.transition(LoaderState.POSTINITIALIZATION, false); modController.transition(LoaderState.POSTINITIALIZATION, false);
modController.distributeStateMessage(FMLInterModComms.IMCEvent.class); modController.distributeStateMessage(FMLInterModComms.IMCEvent.class);
ItemStackHolderInjector.INSTANCE.inject();
modController.distributeStateMessage(LoaderState.POSTINITIALIZATION); modController.distributeStateMessage(LoaderState.POSTINITIALIZATION);
progressBar.step("Finishing up"); progressBar.step("Finishing up");
modController.transition(LoaderState.AVAILABLE, false); modController.transition(LoaderState.AVAILABLE, false);

View file

@ -71,7 +71,7 @@ public class ProgressManager
{ {
if(step >= steps) throw new IllegalStateException("too much steps for ProgressBar " + title); if(step >= steps) throw new IllegalStateException("too much steps for ProgressBar " + title);
step++; step++;
this.message = message; this.message = FMLCommonHandler.instance().stripSpecialChars(message);
FMLCommonHandler.instance().processWindowMessages(); FMLCommonHandler.instance().processWindowMessages();
} }

View file

@ -65,7 +65,7 @@ public class EventSubscriptionTransformer implements IClassTransformer
{ {
if (buildEvents(classNode)) if (buildEvents(classNode))
{ {
ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
classNode.accept(cw); classNode.accept(cw);
return cw.toByteArray(); return cw.toByteArray();
} }

View file

@ -100,7 +100,7 @@ public class MarkerTransformer implements IClassTransformer
@Override @Override
public byte[] transform(String name, String transformedName, byte[] bytes) public byte[] transform(String name, String transformedName, byte[] bytes)
{ {
if (bytes == null) { return null; } if (bytes == null) { return null; }
if (!markers.containsKey(name)) { return bytes; } if (!markers.containsKey(name)) { return bytes; }
ClassNode classNode = new ClassNode(); ClassNode classNode = new ClassNode();

View file

@ -34,7 +34,7 @@ public class SideTransformer implements IClassTransformer
@Override @Override
public byte[] transform(String name, String transformedName, byte[] bytes) public byte[] transform(String name, String transformedName, byte[] bytes)
{ {
if (bytes == null) { return null; } if (bytes == null) { return null; }
ClassNode classNode = new ClassNode(); ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes); ClassReader classReader = new ClassReader(bytes);

View file

@ -32,6 +32,10 @@ import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager; import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.FurnaceRecipes; import net.minecraft.item.crafting.FurnaceRecipes;
import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.IRecipe;
import net.minecraft.nbt.JsonToNBT;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTException;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -47,6 +51,8 @@ import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -451,4 +457,70 @@ public class GameRegistry
*/ */
String value(); String value();
} }
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ItemStackHolder
{
/**
* The registry name of the item being looked up.
* @return The registry name
*/
public String value();
/**
* The metadata or damage value for the itemstack, defaults to 0.
* @return the metadata value
*/
public int meta() default 0;
/**
* The string serialized nbt value for the itemstack. Defaults to empty for no nbt.
*
* @return a nbt string
*/
public String nbt() default "";
}
/**
* Makes an {@link ItemStack} based on the itemName reference, with supplied meta, stackSize and nbt, if possible
*
* Will return null if the item doesn't exist (because it's not from a loaded mod for example)
* Will throw a {@link RuntimeException} if the nbtString is invalid for use in an {@link ItemStack}
*
* @param itemName a registry name reference
* @param meta the meta
* @param stackSize the stack size
* @param nbtString an nbt stack as a string, will be processed by {@link JsonToNBT}
* @return a new itemstack
*/
public static ItemStack makeItemStack(String itemName, int meta, int stackSize, String nbtString)
{
if (itemName == null) throw new IllegalArgumentException("The itemName cannot be null");
Item item = GameData.getItemRegistry().getObject(itemName);
if (item == null) {
FMLLog.getLogger().log(Level.TRACE, "Unable to find item with name {}", itemName);
return null;
}
ItemStack is = new ItemStack(item,1,meta);
if (!Strings.isNullOrEmpty(nbtString)) {
NBTBase nbttag = null;
try
{
nbttag = JsonToNBT.func_180713_a(nbtString);
} catch (NBTException e)
{
FMLLog.getLogger().log(Level.WARN, "Encountered an exception parsing ItemStack NBT string {}", nbtString, e);
throw Throwables.propagate(e);
}
if (!(nbttag instanceof NBTTagCompound)) {
FMLLog.getLogger().log(Level.WARN, "Unexpected NBT string - multiple values {}", nbtString);
throw new RuntimeException("Invalid NBT JSON");
} else {
is.setTagCompound((NBTTagCompound) nbttag);
}
}
return is;
}
} }

View file

@ -0,0 +1,81 @@
package net.minecraftforge.fml.common.registry;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.discovery.ASMDataTable;
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;
import org.apache.logging.log4j.Level;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public enum ItemStackHolderInjector
{
INSTANCE;
private List<ItemStackHolderRef> itemStackHolders = Lists.newArrayList();
public void inject() {
FMLLog.getLogger().log(Level.INFO, "Injecting itemstacks");
for (ItemStackHolderRef ishr: itemStackHolders) {
ishr.apply();
}
FMLLog.getLogger().log(Level.INFO, "Itemstack injection complete");
}
public void findHolders(ASMDataTable table) {
FMLLog.info("Identifying ItemStackHolder annotations");
Set<ASMData> allItemStackHolders = table.getAll(GameRegistry.ItemStackHolder.class.getName());
Map<String, Class<?>> classCache = Maps.newHashMap();
for (ASMData data : allItemStackHolders)
{
String className = data.getClassName();
String annotationTarget = data.getObjectName();
String value = (String) data.getAnnotationInfo().get("value");
int meta = data.getAnnotationInfo().containsKey("meta") ? (Integer) data.getAnnotationInfo().get("meta") : 0;
String nbt = data.getAnnotationInfo().containsKey("nbt") ? (String) data.getAnnotationInfo().get("nbt") : "";
addHolder(classCache, className, annotationTarget, value, meta, nbt);
}
FMLLog.info("Found %d ItemStackHolder annotations", allItemStackHolders.size());
}
private void addHolder(Map<String, Class<?>> classCache, String className, String annotationTarget, String value, Integer meta, String nbt)
{
Class<?> clazz;
if (classCache.containsKey(className))
{
clazz = classCache.get(className);
}
else
{
try
{
clazz = Class.forName(className, true, getClass().getClassLoader());
classCache.put(className, clazz);
}
catch (Exception ex)
{
// unpossible?
throw Throwables.propagate(ex);
}
}
try
{
Field f = clazz.getField(annotationTarget);
itemStackHolders.add(new ItemStackHolderRef(f, value, meta, nbt));
}
catch (Exception ex)
{
// unpossible?
throw Throwables.propagate(ex);
}
}
}

View file

@ -0,0 +1,83 @@
package net.minecraftforge.fml.common.registry;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.logging.log4j.Level;
import com.google.common.base.Throwables;
/**
* Internal class used in tracking {@link ItemStackHolder} references
*
* @author cpw
*
*/
class ItemStackHolderRef {
private Field field;
private String itemName;
private int meta;
private String serializednbt;
ItemStackHolderRef(Field field, String itemName, int meta, String serializednbt)
{
this.field = field;
this.itemName = itemName;
this.meta = meta;
this.serializednbt = serializednbt;
makeWritable(field);
}
private static Field modifiersField;
private static Object reflectionFactory;
private static Method newFieldAccessor;
private static Method fieldAccessorSet;
private static void makeWritable(Field f)
{
try
{
if (modifiersField == null)
{
Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory");
reflectionFactory = getReflectionFactory.invoke(null);
newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class);
fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class);
modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
}
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
} catch (Exception e)
{
throw Throwables.propagate(e);
}
}
public void apply()
{
ItemStack is;
try
{
is = GameRegistry.makeItemStack(itemName, meta, 1, serializednbt);
} catch (RuntimeException e)
{
FMLLog.getLogger().log(Level.ERROR, "Caught exception processing itemstack {},{},{} in annotation at {}.{}", itemName, meta, serializednbt,field.getClass().getName(),field.getName());
throw e;
}
try
{
Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false);
fieldAccessorSet.invoke(fieldAccessor, null, is);
}
catch (Exception e)
{
FMLLog.getLogger().log(Level.WARN, "Unable to set {} with value {},{},{}", this.field, this.itemName, this.meta, this.serializednbt);
}
}
}

View file

@ -272,4 +272,10 @@ public class FMLServerHandler implements IFMLSidedHandler
{ {
// NOOP // NOOP
} }
@Override
public String stripSpecialChars(String message)
{
return message;
}
} }

View file

@ -17,7 +17,7 @@
boolean flag1 = (c0 == 0 || j == -1 || this.field_78293_l) && p_78255_2_; boolean flag1 = (c0 == 0 || j == -1 || this.field_78293_l) && p_78255_2_;
if (flag1) if (flag1)
@@ -583,11 +584,6 @@ @@ -592,11 +593,6 @@
int j = this.field_78287_e[p_78263_1_] >>> 4; int j = this.field_78287_e[p_78263_1_] >>> 4;
int k = this.field_78287_e[p_78263_1_] & 15; int k = this.field_78287_e[p_78263_1_] & 15;

View file

@ -13,7 +13,7 @@ public class FMLForgePlugin implements IFMLLoadingPlugin
@Override @Override
public String[] getASMTransformerClass() public String[] getASMTransformerClass()
{ {
return new String[0]; return new String[] { "net.minecraftforge.classloading.FluidIdTransformer" };
} }
@Override @Override

View file

@ -0,0 +1,55 @@
package net.minecraftforge.classloading;
import java.util.ListIterator;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import net.minecraftforge.fml.common.FMLLog;;
public class FluidIdTransformer implements IClassTransformer {
private static final String FLUID_TYPE = "net/minecraftforge/fluids/FluidStack";
private static final String GETID_NAME = "getFluidID";
private static final String LEGACY_FIELDNAME = "fluidID";
private static final String GETID_DESC = "()I";
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null)
return null;
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(basicClass);
classReader.accept(classNode, 0);
for (MethodNode m: classNode.methods)
{
for (ListIterator<AbstractInsnNode> it = m.instructions.iterator(); it.hasNext(); )
{
AbstractInsnNode insnNode = it.next();
if (insnNode.getType() == AbstractInsnNode.FIELD_INSN)
{
FieldInsnNode fi = (FieldInsnNode)insnNode;
if (FLUID_TYPE.equals(fi.owner) && LEGACY_FIELDNAME.equals(fi.name) && fi.getOpcode() == Opcodes.GETFIELD)
{
FMLLog.fine("Method %s.%s%s: Replacing GETFIELD fluidID with INVOKEVIRTUAL getFluidID", name, m.name, m.desc);
it.remove();
MethodInsnNode replace = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, FLUID_TYPE, GETID_NAME, GETID_DESC, false);
it.add(replace);
}
}
}
}
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classNode.accept(writer);
return writer.toByteArray();
}
}

View file

@ -663,7 +663,7 @@ public class ForgeChunkManager
throw new RuntimeException("Invalid ticket request"); throw new RuntimeException("Invalid ticket request");
} }
int allowedCount = ticketConstraints.containsKey(modId) ? ticketConstraints.get(modId) : defaultMaxCount; int allowedCount = getMaxTicketLengthFor(modId);
if (tickets.get(world).get(modId).size() >= allowedCount) if (tickets.get(world).get(modId).size() >= allowedCount)
{ {

View file

@ -26,6 +26,7 @@ import net.minecraftforge.classloading.FMLForgePlugin;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property; import net.minecraftforge.common.config.Property;
import net.minecraftforge.common.network.ForgeNetworkHandler; import net.minecraftforge.common.network.ForgeNetworkHandler;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.oredict.OreDictionary;
import net.minecraftforge.oredict.RecipeSorter; import net.minecraftforge.oredict.RecipeSorter;
import net.minecraftforge.server.command.ForgeCommand; import net.minecraftforge.server.command.ForgeCommand;
@ -60,7 +61,6 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
public static boolean removeErroringEntities = false; public static boolean removeErroringEntities = false;
public static boolean removeErroringTileEntities = false; public static boolean removeErroringTileEntities = false;
public static boolean disableStitchedFileSaving = false; public static boolean disableStitchedFileSaving = false;
public static boolean forceDuplicateFluidBlockCrash = true;
public static boolean fullBoundingBoxLadders = false; public static boolean fullBoundingBoxLadders = false;
public static double zombieSummonBaseChance = 0.1; public static double zombieSummonBaseChance = 0.1;
public static int[] blendRanges = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34 }; public static int[] blendRanges = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34 };
@ -68,6 +68,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
public static boolean shouldSortRecipies = true; public static boolean shouldSortRecipies = true;
public static boolean disableVersionCheck = false; public static boolean disableVersionCheck = false;
public static int defaultSpawnFuzz = 20; public static int defaultSpawnFuzz = 20;
public static boolean defaultHasSpawnFuzz = true;
private static Configuration config; private static Configuration config;
@ -157,17 +158,6 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
shouldSortRecipies = prop.getBoolean(shouldSortRecipies); shouldSortRecipies = prop.getBoolean(shouldSortRecipies);
propOrder.add(prop.getName()); propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_GENERAL, "forceDuplicateFluidBlockCrash", true);
prop.comment = "Set this to true to force a crash if more than one block attempts to link back to the same Fluid. Enabled by default.";
prop.setLanguageKey("forge.configgui.forceDuplicateFluidBlockCrash").setRequiresMcRestart(true);
forceDuplicateFluidBlockCrash = prop.getBoolean(true);
propOrder.add(prop.getName());
if (!forceDuplicateFluidBlockCrash)
{
FMLLog.warning("Disabling forced crashes on duplicate Fluid Blocks - USE AT YOUR OWN RISK");
}
prop = config.get(Configuration.CATEGORY_GENERAL, "removeErroringEntities", false); prop = config.get(Configuration.CATEGORY_GENERAL, "removeErroringEntities", false);
prop.comment = "Set this to true to remove any Entity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES."; prop.comment = "Set this to true to remove any Entity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.";
prop.setLanguageKey("forge.configgui.removeErroringEntities").setRequiresWorldRestart(true); prop.setLanguageKey("forge.configgui.removeErroringEntities").setRequiresWorldRestart(true);
@ -225,6 +215,12 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
defaultSpawnFuzz = prop.getInt(20); defaultSpawnFuzz = prop.getInt(20);
propOrder.add(prop.getName()); propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_GENERAL, "spawnHasFuzz", Boolean.TRUE,
"If the overworld has ANY spawn fuzz at all. If not, the spawn will always be the exact same location.");
prop.setLanguageKey("forge.configgui.hasspawnfuzz").setRequiresWorldRestart(false);
defaultHasSpawnFuzz = prop.getBoolean(Boolean.TRUE);
propOrder.add(prop.getName());
config.setCategoryPropertyOrder(CATEGORY_GENERAL, propOrder); config.setCategoryPropertyOrder(CATEGORY_GENERAL, propOrder);
if (config.hasChanged()) if (config.hasChanged())
@ -280,7 +276,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER); MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER);
ForgeChunkManager.captureConfig(evt.getModConfigurationDirectory()); ForgeChunkManager.captureConfig(evt.getModConfigurationDirectory());
FMLCommonHandler.instance().bus().register(this); FMLCommonHandler.instance().bus().register(this);
if (!ForgeModContainer.disableVersionCheck) if (!ForgeModContainer.disableVersionCheck)
{ {
ForgeVersion.startVersionCheck(); ForgeVersion.startVersionCheck();
@ -301,6 +297,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
{ {
RecipeSorter.sortCraftManager(); RecipeSorter.sortCraftManager();
} }
FluidRegistry.validateFluidRegistry();
} }
@Subscribe @Subscribe
@ -314,6 +311,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
NBTTagCompound forgeData = new NBTTagCompound(); NBTTagCompound forgeData = new NBTTagCompound();
NBTTagCompound dimData = DimensionManager.saveDimensionDataMap(); NBTTagCompound dimData = DimensionManager.saveDimensionDataMap();
forgeData.setTag("DimensionData", dimData); forgeData.setTag("DimensionData", dimData);
FluidRegistry.writeDefaultFluidList(forgeData);
return forgeData; return forgeData;
} }
@ -321,6 +319,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
public void readData(SaveHandler handler, WorldInfo info, Map<String, NBTBase> propertyMap, NBTTagCompound tag) public void readData(SaveHandler handler, WorldInfo info, Map<String, NBTBase> propertyMap, NBTTagCompound tag)
{ {
DimensionManager.loadDimensionDataMap(tag.hasKey("DimensionData") ? tag.getCompoundTag("DimensionData") : null); DimensionManager.loadDimensionDataMap(tag.hasKey("DimensionData") ? tag.getCompoundTag("DimensionData") : null);
FluidRegistry.loadFluidDefaults(tag);
} }
@Subscribe @Subscribe

View file

@ -12,7 +12,7 @@ public class FluidIdRegistryMessageHandler extends SimpleChannelInboundHandler<F
@Override @Override
protected void channelRead0(ChannelHandlerContext ctx, ForgeMessage.FluidIdMapMessage msg) throws Exception protected void channelRead0(ChannelHandlerContext ctx, ForgeMessage.FluidIdMapMessage msg) throws Exception
{ {
FluidRegistry.initFluidIDs(msg.fluidIds); FluidRegistry.initFluidIDs(msg.fluidIds, msg.defaultFluids);
} }
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception

View file

@ -1,12 +1,18 @@
package net.minecraftforge.common.network; package net.minecraftforge.common.network;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.Level;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidRegistry;
import com.google.common.collect.BiMap; import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap; import com.google.common.collect.HashBiMap;
import com.google.common.collect.Sets;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.network.ByteBufUtils; import net.minecraftforge.fml.common.network.ByteBufUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -16,14 +22,14 @@ public abstract class ForgeMessage {
int dimensionId; int dimensionId;
/** The provider ID to register with dimension on client */ /** The provider ID to register with dimension on client */
int providerId; int providerId;
public DimensionRegisterMessage(){} public DimensionRegisterMessage(){}
public DimensionRegisterMessage(int dimensionId, int providerId) public DimensionRegisterMessage(int dimensionId, int providerId)
{ {
this.dimensionId = dimensionId; this.dimensionId = dimensionId;
this.providerId = providerId; this.providerId = providerId;
} }
@Override @Override
void toBytes(ByteBuf bytes) void toBytes(ByteBuf bytes)
{ {
@ -40,17 +46,23 @@ public abstract class ForgeMessage {
} }
public static class FluidIdMapMessage extends ForgeMessage { public static class FluidIdMapMessage extends ForgeMessage {
BiMap<String, Integer> fluidIds = HashBiMap.create(); BiMap<Fluid, Integer> fluidIds = HashBiMap.create();
Set<String> defaultFluids = Sets.newHashSet();
@Override @Override
void toBytes(ByteBuf bytes) void toBytes(ByteBuf bytes)
{ {
Map<String, Integer> ids = FluidRegistry.getRegisteredFluidIDs(); Map<Fluid, Integer> ids = FluidRegistry.getRegisteredFluidIDsByFluid();
bytes.writeInt(ids.size()); bytes.writeInt(ids.size());
for (Map.Entry<String, Integer> entry : ids.entrySet()) for (Map.Entry<Fluid, Integer> entry : ids.entrySet())
{ {
ByteBufUtils.writeUTF8String(bytes,entry.getKey()); ByteBufUtils.writeUTF8String(bytes,entry.getKey().getName());
bytes.writeInt(entry.getValue()); bytes.writeInt(entry.getValue());
} }
for (Map.Entry<Fluid, Integer> entry : ids.entrySet())
{
String defaultName = FluidRegistry.getDefaultFluidName(entry.getKey());
ByteBufUtils.writeUTF8String(bytes, defaultName);
}
} }
@Override @Override
@ -60,7 +72,21 @@ public abstract class ForgeMessage {
for (int i = 0; i < listSize; i++) { for (int i = 0; i < listSize; i++) {
String fluidName = ByteBufUtils.readUTF8String(bytes); String fluidName = ByteBufUtils.readUTF8String(bytes);
int fluidId = bytes.readInt(); int fluidId = bytes.readInt();
fluidIds.put(fluidName, fluidId); 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();
} }
} }
} }

View file

@ -96,6 +96,13 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
protected final String fluidName; protected final String fluidName;
/**
* This is the fluid used in the constructor. Use this reference to configure things
* like icons for your block. It might not be active in the registry, so do
* NOT expose it.
*/
protected final Fluid definedFluid;
public BlockFluidBase(Fluid fluid, Material material) public BlockFluidBase(Fluid fluid, Material material)
{ {
super(material); super(material);
@ -111,6 +118,7 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
this.densityDir = fluid.density > 0 ? -1 : 1; this.densityDir = fluid.density > 0 ? -1 : 1;
fluid.setBlock(this); fluid.setBlock(this);
this.definedFluid = fluid;
displacements.putAll(defaultDisplacements); displacements.putAll(defaultDisplacements);
this.setDefaultState(blockState.getBaseState().withProperty(LEVEL, 0)); this.setDefaultState(blockState.getBaseState().withProperty(LEVEL, 0));
} }

View file

@ -66,13 +66,17 @@ public class Fluid
* *
* Default value is approximately the real-life room temperature of water in degrees Kelvin. * Default value is approximately the real-life room temperature of water in degrees Kelvin.
*/ */
protected int temperature = 295; protected int temperature = 300;
/** /**
* Viscosity ("thickness") of the fluid - completely arbitrary; negative values are not * Viscosity ("thickness") of the fluid - completely arbitrary; negative values are not
* permissible. * permissible.
* *
* Default value is approximately the real-life density of water in m/s^2 (x10^-3). * Default value is approximately the real-life density of water in m/s^2 (x10^-3).
*
* Higher viscosity means that a fluid flows more slowly, like molasses.
* Lower viscosity means that a fluid flows more quickly, like helium.
*
*/ */
protected int viscosity = 1000; protected int viscosity = 1000;
@ -117,17 +121,10 @@ public class Fluid
{ {
this.block = block; this.block = block;
} }
else if (!ForgeModContainer.forceDuplicateFluidBlockCrash)
{
FMLLog.warning("A mod has attempted to assign Block " + block + " to the Fluid '" + fluidName + "' but this Fluid has already been linked to BlockID "
+ this.block + ". Configure your mods to prevent this from happening.");
}
else else
{ {
FMLLog.severe("A mod has attempted to assign BlockID " + block + " to the Fluid '" + fluidName + "' but this Fluid has already been linked to BlockID " FMLLog.warning("A mod has attempted to assign Block " + block + " to the Fluid '" + fluidName + "' but this Fluid has already been linked to the Block "
+ this.block + ". Configure your mods to prevent this from happening."); + this.block + ". You may have duplicate Fluid Blocks as a result. It *may* be possible to configure your mods to avoid this.");
throw new LoaderException(new RuntimeException("A mod has attempted to assign BlockID " + block + " to the Fluid '" + fluidName
+ "' but this Fluid has already been linked to BlockID " + this.block + ". Configure your mods to prevent this from happening."));
} }
return this; return this;
} }
@ -320,19 +317,4 @@ public class Fluid
public int getColor(World world, BlockPos pos){ return getColor(); } public int getColor(World world, BlockPos pos){ return getColor(); }
public TextureAtlasSprite getIcon(World world, BlockPos pos){ return getIcon(); } public TextureAtlasSprite getIcon(World world, BlockPos pos){ return getIcon(); }
private static Map<String, String> legacyNames = Maps.newHashMap();
static String convertLegacyName(String fluidName)
{
return fluidName != null && legacyNames.containsKey(fluidName) ? legacyNames.get(fluidName) : fluidName;
}
/**
* Register a legacy liquid name with the Fluids system
* @param legacyName The legacy name to recognize
* @param canonicalName The canonical fluid name it will become
*/
public static void registerLegacyName(String legacyName, String canonicalName)
{
legacyNames.put(legacyName.toLowerCase(Locale.ENGLISH), canonicalName);
}
} }

View file

@ -11,6 +11,7 @@ import java.util.Set;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.eventhandler.Event; import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.init.Items; import net.minecraft.init.Items;
import net.minecraft.item.Item; import net.minecraft.item.Item;
@ -20,16 +21,16 @@ import net.minecraftforge.common.MinecraftForge;
/** /**
* Register simple items that contain fluids here. Useful for buckets, bottles, and things that have * Register simple items that contain fluids here. Useful for buckets, bottles, and things that have
* ID/metadata mappings. * ID/metadata mappings.
* *
* For more complex items, use {@link IFluidContainerItem} instead. * For more complex items, use {@link IFluidContainerItem} instead.
* *
* @author King Lemming * @author King Lemming
* *
*/ */
public abstract class FluidContainerRegistry public abstract class FluidContainerRegistry
{ {
// Holder object that implements HashCode for an ItemStack, // Holder object that implements HashCode for an ItemStack,
// the local maps are not guaranteed to have the same internal generic structure, // the local maps are not guaranteed to have the same internal generic structure,
// but the external interface for checking ItemStacks will still exist. // but the external interface for checking ItemStacks will still exist.
private static class ContainerKey private static class ContainerKey
{ {
@ -51,7 +52,7 @@ public abstract class FluidContainerRegistry
code = 31*code + container.getItem().hashCode(); code = 31*code + container.getItem().hashCode();
code = 31*code + container.getItemDamage(); code = 31*code + container.getItemDamage();
if (fluid != null) if (fluid != null)
code = 31*code + fluid.fluidID; code = 31*code + fluid.getFluid().hashCode();
return code; return code;
} }
@Override @Override
@ -64,7 +65,7 @@ public abstract class FluidContainerRegistry
if (fluid == null && ck.fluid != null) return false; if (fluid == null && ck.fluid != null) return false;
if (fluid != null && ck.fluid == null) return false; if (fluid != null && ck.fluid == null) return false;
if (fluid == null && ck.fluid == null) return true; if (fluid == null && ck.fluid == null) return true;
if (fluid.fluidID != ck.fluid.fluidID) return false; if (fluid.getFluid() != ck.fluid.getFluid()) return false;
return true; return true;
} }
} }
@ -89,7 +90,7 @@ public abstract class FluidContainerRegistry
/** /**
* Register a new fluid containing item. * Register a new fluid containing item.
* *
* @param stack * @param stack
* FluidStack containing the type and amount of the fluid stored in the item. * FluidStack containing the type and amount of the fluid stored in the item.
* @param filledContainer * @param filledContainer
@ -106,7 +107,7 @@ public abstract class FluidContainerRegistry
/** /**
* Register a new fluid containing item. The item is assumed to hold 1000 mB of fluid. Also * Register a new fluid containing item. The item is assumed to hold 1000 mB of fluid. Also
* registers the Fluid if possible. * registers the Fluid if possible.
* *
* @param fluid * @param fluid
* Fluid type that is stored in the item. * Fluid type that is stored in the item.
* @param filledContainer * @param filledContainer
@ -126,12 +127,12 @@ public abstract class FluidContainerRegistry
/** /**
* Register a new fluid containing item that does not have an empty container. * Register a new fluid containing item that does not have an empty container.
* *
* @param stack * @param stack
* FluidStack containing the type and amount of the fluid stored in the item. * FluidStack containing the type and amount of the fluid stored in the item.
* @param filledContainer * @param filledContainer
* ItemStack representing the container when it is full. * ItemStack representing the container when it is full.
* @return True if container was successfully registered; false if it already is. * @return True if container was successfully registered; false if it already is, or an invalid parameter was passed.
*/ */
public static boolean registerFluidContainer(FluidStack stack, ItemStack filledContainer) public static boolean registerFluidContainer(FluidStack stack, ItemStack filledContainer)
{ {
@ -141,12 +142,12 @@ public abstract class FluidContainerRegistry
/** /**
* Register a new fluid containing item that does not have an empty container. The item is * Register a new fluid containing item that does not have an empty container. The item is
* assumed to hold 1000 mB of fluid. Also registers the Fluid if possible. * assumed to hold 1000 mB of fluid. Also registers the Fluid if possible.
* *
* @param fluid * @param fluid
* Fluid type that is stored in the item. * Fluid type that is stored in the item.
* @param filledContainer * @param filledContainer
* ItemStack representing the container when it is full. * ItemStack representing the container when it is full.
* @return True if container was successfully registered; false if it already is. * @return True if container was successfully registered; false if it already is, or an invalid parameter was passed.
*/ */
public static boolean registerFluidContainer(Fluid fluid, ItemStack filledContainer) public static boolean registerFluidContainer(Fluid fluid, ItemStack filledContainer)
{ {
@ -159,17 +160,22 @@ public abstract class FluidContainerRegistry
/** /**
* Register a new fluid containing item. * Register a new fluid containing item.
* *
* @param data * @param data
* See {@link FluidContainerData}. * See {@link FluidContainerData}.
* @return True if container was successfully registered; false if it already is. * @return True if container was successfully registered; false if it already is, or an invalid parameter was passed.
*/ */
public static boolean registerFluidContainer(FluidContainerData data) public static boolean registerFluidContainer(FluidContainerData data)
{ {
if (isFilledContainer(data.filledContainer)) if (isFilledContainer(data.filledContainer) || data.filledContainer == null)
{ {
return false; return false;
} }
if (data.fluid == null || data.fluid.getFluid() == null)
{
FMLLog.bigWarning("Invalid registration attempt for a fluid container item %s has occurred. The registration has been denied to prevent crashes. The mod responsible for the registration needs to correct this.", data.filledContainer.getItem().getUnlocalizedName(data.filledContainer));
return false;
}
containerFluidMap.put(new ContainerKey(data.filledContainer), data); containerFluidMap.put(new ContainerKey(data.filledContainer), data);
if (data.emptyContainer != null && data.emptyContainer != NULL_EMPTYCONTAINER) if (data.emptyContainer != null && data.emptyContainer != NULL_EMPTYCONTAINER)
@ -184,7 +190,7 @@ public abstract class FluidContainerRegistry
/** /**
* Determines the fluid type and amount inside a container. * Determines the fluid type and amount inside a container.
* *
* @param container * @param container
* The fluid container. * The fluid container.
* @return FluidStack representing stored fluid. * @return FluidStack representing stored fluid.
@ -202,9 +208,9 @@ public abstract class FluidContainerRegistry
/** /**
* Attempts to fill an empty container with a fluid. * Attempts to fill an empty container with a fluid.
* *
* NOTE: Returns null on fail, NOT the empty container. * NOTE: Returns null on fail, NOT the empty container.
* *
* @param fluid * @param fluid
* FluidStack containing the type and amount of fluid to fill. * FluidStack containing the type and amount of fluid to fill.
* @param container * @param container
@ -228,7 +234,7 @@ public abstract class FluidContainerRegistry
/** /**
* Attempts to empty a full container. * Attempts to empty a full container.
* *
* @param container * @param container
* ItemStack representing the full container. * ItemStack representing the full container.
* @return Empty container if successful, otherwise null. * @return Empty container if successful, otherwise null.
@ -251,7 +257,7 @@ public abstract class FluidContainerRegistry
/** /**
* Determines the capacity of a full container. * Determines the capacity of a full container.
* *
* @param container * @param container
* The full container. * The full container.
* @return The containers capacity, or 0 if the ItemStack does not represent * @return The containers capacity, or 0 if the ItemStack does not represent
@ -264,7 +270,7 @@ public abstract class FluidContainerRegistry
/** /**
* Determines the capacity of a container. * Determines the capacity of a container.
* *
* @param fluid * @param fluid
* FluidStack containing the type of fluid the capacity should be * FluidStack containing the type of fluid the capacity should be
* determined for (ignored for full containers). * determined for (ignored for full containers).
@ -361,7 +367,7 @@ public abstract class FluidContainerRegistry
public final ItemStack filledContainer; public final ItemStack filledContainer;
public final ItemStack emptyContainer; public final ItemStack emptyContainer;
public FluidContainerData(FluidStack stack, ItemStack filledContainer, ItemStack emptyContainer) public FluidContainerData(FluidStack stack, ItemStack filledContainer, ItemStack emptyContainer)
{ {
this(stack, filledContainer, emptyContainer, false); this(stack, filledContainer, emptyContainer, false);

View file

@ -1,19 +1,31 @@
package net.minecraftforge.fluids; package net.minecraftforge.fluids;
import java.util.HashMap;
import java.util.Map; 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.block.Block;
import net.minecraft.init.Blocks; 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.minecraft.util.StatCollector;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap; import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap; import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.eventhandler.Event; import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.registry.RegistryDelegate;
/** /**
* Handles Fluid registrations. Fluids MUST be registered in order to function. * Handles Fluid registrations. Fluids MUST be registered in order to function.
@ -25,17 +37,23 @@ public abstract class FluidRegistry
{ {
static int maxID = 0; static int maxID = 0;
static HashMap<String, Fluid> fluids = Maps.newHashMap(); static BiMap<String, Fluid> fluids = HashBiMap.create();
static BiMap<String, Integer> fluidIDs = HashBiMap.create(); static BiMap<Fluid, Integer> fluidIDs = HashBiMap.create();
static BiMap<Integer, String> fluidNames = HashBiMap.create(); //Caching this just makes some other calls faster
static BiMap<Block, Fluid> fluidBlocks; static BiMap<Block, Fluid> fluidBlocks;
// the globally unique fluid map - only used to associate non-defaults during world/server loading
static BiMap<String,Fluid> masterFluidReference = HashBiMap.create();
static BiMap<String,String> defaultFluidName = HashBiMap.create();
static Map<Fluid,FluidDelegate> delegates = Maps.newHashMap();
public static final Fluid WATER = new Fluid("water") { public static final Fluid WATER = new Fluid("water") {
@Override @Override
public String getLocalizedName() { public String getLocalizedName() {
return StatCollector.translateToLocal("tile.water.name"); return StatCollector.translateToLocal("tile.water.name");
} }
}.setBlock(Blocks.water).setUnlocalizedName(Blocks.water.getUnlocalizedName()); }.setBlock(Blocks.water).setUnlocalizedName(Blocks.water.getUnlocalizedName());
public static final Fluid LAVA = new Fluid("lava") { public static final Fluid LAVA = new Fluid("lava") {
@Override @Override
public String getLocalizedName() { public String getLocalizedName() {
@ -57,41 +75,109 @@ public abstract class FluidRegistry
* Called by Forge to prepare the ID map for server -> client sync. * Called by Forge to prepare the ID map for server -> client sync.
* Modders, DO NOT call this. * Modders, DO NOT call this.
*/ */
public static void initFluidIDs(BiMap<String, Integer> newfluidIDs) public static void initFluidIDs(BiMap<Fluid, Integer> newfluidIDs, Set<String> defaultNames)
{ {
maxID = newfluidIDs.size(); maxID = newfluidIDs.size();
fluidIDs.clear(); fluidIDs.clear();
fluidIDs.putAll(newfluidIDs); fluidIDs.putAll(newfluidIDs);
fluidNames.clear();
for (Entry<Fluid, Integer> 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<String> 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());
Fluid oldFluid = fluids.put(fluid.getName(), fluid);
Integer id = fluidIDs.remove(oldFluid);
fluidIDs.put(fluid, id);
}
fluidBlocks = null;
for (FluidDelegate fd : delegates.values())
{
fd.rebind();
}
}
/**
* 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 * @param fluid
* The fluid to register. * 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) public static boolean registerFluid(Fluid fluid)
{ {
if (fluidIDs.containsKey(fluid.getName())) masterFluidReference.put(uniqueName(fluid), fluid);
delegates.put(fluid, new FluidDelegate(fluid, fluid.getName()));
if (fluids.containsKey(fluid.getName()))
{ {
return false; return false;
} }
fluids.put(fluid.getName(), fluid); fluids.put(fluid.getName(), fluid);
fluidIDs.put(fluid.getName(), ++maxID); 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)); MinecraftForge.EVENT_BUS.post(new FluidRegisterEvent(fluid.getName(), maxID));
return true; 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) public static boolean isFluidRegistered(Fluid fluid)
{ {
return fluidIDs.containsKey(fluid.getName()); return fluid != null && fluids.containsKey(fluid.getName());
} }
public static boolean isFluidRegistered(String fluidName) public static boolean isFluidRegistered(String fluidName)
{ {
return fluidIDs.containsKey(fluidName); return fluids.containsKey(fluidName);
} }
public static Fluid getFluid(String fluidName) public static Fluid getFluid(String fluidName)
@ -101,31 +187,42 @@ public abstract class FluidRegistry
public static Fluid getFluid(int fluidID) public static Fluid getFluid(int fluidID)
{ {
return fluids.get(getFluidName(fluidID)); return fluidIDs.inverse().get(fluidID);
} }
public static String getFluidName(int fluidID) public static int getFluidID(Fluid fluid)
{ {
return fluidIDs.inverse().get(fluidID); return fluidIDs.get(fluid);
}
public static String getFluidName(FluidStack stack)
{
return getFluidName(stack.fluidID);
} }
public static int getFluidID(String fluidName) public static int getFluidID(String fluidName)
{ {
return fluidIDs.get(fluidName); return fluidIDs.get(getFluid(fluidName));
}
@Deprecated //Remove in 1.8.3
public static String getFluidName(int fluidID)
{
return fluidNames.get(fluidID);
}
public static String getFluidName(Fluid fluid)
{
return fluids.inverse().get(fluid);
}
public static String getFluidName(FluidStack stack)
{
return getFluidName(stack.getFluid());
} }
public static FluidStack getFluidStack(String fluidName, int amount) public static FluidStack getFluidStack(String fluidName, int amount)
{ {
if (!fluidIDs.containsKey(fluidName)) if (!fluids.containsKey(fluidName))
{ {
return null; return null;
} }
return new FluidStack(getFluidID(fluidName), amount); return new FluidStack(getFluid(fluidName), amount);
} }
/** /**
@ -139,7 +236,17 @@ public abstract class FluidRegistry
/** /**
* Returns a read-only map containing Fluid Names and their associated IDs. * Returns a read-only map containing Fluid Names and their associated IDs.
*/ */
@Deprecated //Change return type to <Fluid, Integer> in 1.8.3
public static Map<String, Integer> getRegisteredFluidIDs() public static Map<String, Integer> getRegisteredFluidIDs()
{
return ImmutableMap.copyOf(fluidNames.inverse());
}
/**
* Returns a read-only map containing Fluid IDs and their associated Fluids.
* In 1.8.3, this will change to just 'getRegisteredFluidIDs'
*/
public static Map<Fluid, Integer> getRegisteredFluidIDsByFluid()
{ {
return ImmutableMap.copyOf(fluidIDs); return ImmutableMap.copyOf(fluidIDs);
} }
@ -160,7 +267,7 @@ public abstract class FluidRegistry
} }
return fluidBlocks.get(block); return fluidBlocks.get(block);
} }
public static class FluidRegisterEvent extends Event public static class FluidRegisterEvent extends Event
{ {
public final String fluidName; public final String fluidName;
@ -177,4 +284,110 @@ public abstract class FluidRegistry
{ {
return maxID; return maxID;
} }
}
public static String getDefaultFluidName(Fluid key)
{
String name = masterFluidReference.inverse().get(key);
if (Strings.isNullOrEmpty(name)) {
FMLLog.getLogger().log(Level.ERROR, "The fluid registry is corrupted. A fluid {} {} is not properly registered. The mod that registered this is broken", key.getClass().getName(), key.getName());
throw new IllegalStateException("The fluid registry is corrupted");
}
return name;
}
public static void loadFluidDefaults(NBTTagCompound tag)
{
Set<String> 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<String, Fluid> def : fluids.entrySet())
{
tagList.appendTag(new NBTTagString(getDefaultFluidName(def.getValue())));
}
forgeData.setTag("DefaultFluidList", tagList);
}
public static void validateFluidRegistry()
{
Set<Fluid> illegalFluids = Sets.newHashSet();
for (Fluid f : fluids.values())
{
if (!masterFluidReference.containsValue(f))
{
illegalFluids.add(f);
}
}
if (!illegalFluids.isEmpty())
{
FMLLog.getLogger().log(Level.FATAL, "The fluid registry is corrupted. Something has inserted a fluid without registering it");
FMLLog.getLogger().log(Level.FATAL, "There is {} unregistered fluids", illegalFluids.size());
for (Fluid f: illegalFluids)
{
FMLLog.getLogger().log(Level.FATAL, " Fluid name : {}, type: {}", f.getName(), f.getClass().getName());
}
FMLLog.getLogger().log(Level.FATAL, "The mods that own these fluids need to register them properly");
throw new IllegalStateException("The fluid map contains fluids unknown to the master fluid registry");
}
}
static RegistryDelegate<Fluid> makeDelegate(Fluid fl)
{
return delegates.get(fl);
}
private static class FluidDelegate implements RegistryDelegate<Fluid>
{
private String name;
private Fluid fluid;
FluidDelegate(Fluid fluid, String name)
{
this.fluid = fluid;
this.name = name;
}
@Override
public Fluid get()
{
return fluid;
}
@Override
public String name()
{
return name;
}
@Override
public Class<Fluid> type()
{
return Fluid.class;
}
void rebind()
{
fluid = fluids.get(name);
}
}
}

View file

@ -1,10 +1,10 @@
package net.minecraftforge.fluids; package net.minecraftforge.fluids;
import java.util.Locale;
import com.google.common.base.Strings;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.registry.RegistryDelegate;
/** /**
* ItemStack substitute for Fluids. * ItemStack substitute for Fluids.
@ -18,25 +18,36 @@ import net.minecraft.nbt.NBTTagCompound;
*/ */
public class FluidStack public class FluidStack
{ {
public int fluidID; /**
* This field will be removed in 1.8. It may be incorrect after a world is loaded. Code should always
* use {@link #getFluid()} instead. That will always reflect the correct value.
*/
@Deprecated
public final Fluid fluid;
public int amount; public int amount;
public NBTTagCompound tag; public NBTTagCompound tag;
private RegistryDelegate<Fluid> fluidDelegate;
public FluidStack(Fluid fluid, int amount) public FluidStack(Fluid fluid, int amount)
{ {
this.fluidID = fluid.getID(); if (fluid == null)
{
FMLLog.bigWarning("Null fluid supplied to fluidstack. Did you try and create a stack for an unregistered fluid?");
throw new IllegalArgumentException("Cannot create a fluidstack from a null fluid");
}
else if (!FluidRegistry.isFluidRegistered(fluid))
{
FMLLog.bigWarning("Failed attempt to create a FluidStack for an unregistered Fluid %s (type %s)", fluid.getName(), fluid.getClass().getName());
throw new IllegalArgumentException("Cannot create a fluidstack from an unregistered fluid");
}
this.fluidDelegate = FluidRegistry.makeDelegate(fluid);
this.amount = amount; this.amount = amount;
this.fluid = fluid;
} }
public FluidStack(int fluidID, int amount) public FluidStack(Fluid fluid, int amount, NBTTagCompound nbt)
{ {
this.fluidID = fluidID; this(fluid, amount);
this.amount = amount;
}
public FluidStack(int fluidID, int amount, NBTTagCompound nbt)
{
this(fluidID, amount);
if (nbt != null) if (nbt != null)
{ {
@ -46,7 +57,21 @@ public class FluidStack
public FluidStack(FluidStack stack, int amount) public FluidStack(FluidStack stack, int amount)
{ {
this(stack.fluidID, amount, stack.tag); this(stack.getFluid(), amount, stack.tag);
}
// To be removed in 1.8
@Deprecated
public FluidStack(int fluidID, int amount)
{
this(FluidRegistry.getFluid(fluidID), amount);
}
// To be removed in 1.8
@Deprecated
public FluidStack(int fluidID, int amount, NBTTagCompound nbt)
{
this(FluidRegistry.getFluid(fluidID), amount, nbt);
} }
/** /**
@ -60,32 +85,23 @@ public class FluidStack
return null; return null;
} }
String fluidName = nbt.getString("FluidName"); String fluidName = nbt.getString("FluidName");
if (Strings.isNullOrEmpty(fluidName))
{
fluidName = nbt.hasKey("LiquidName") ? nbt.getString("LiquidName").toLowerCase(Locale.ENGLISH) : null;
fluidName = Fluid.convertLegacyName(fluidName);
}
if (fluidName ==null || FluidRegistry.getFluid(fluidName) == null) if (fluidName == null || FluidRegistry.getFluid(fluidName) == null)
{ {
return null; return null;
} }
FluidStack stack = new FluidStack(FluidRegistry.getFluidID(fluidName), nbt.getInteger("Amount")); FluidStack stack = new FluidStack(FluidRegistry.getFluid(fluidName), nbt.getInteger("Amount"));
if (nbt.hasKey("Tag")) if (nbt.hasKey("Tag"))
{ {
stack.tag = nbt.getCompoundTag("Tag"); stack.tag = nbt.getCompoundTag("Tag");
} }
else if (nbt.hasKey("extra"))
{
stack.tag = nbt.getCompoundTag("extra");
}
return stack; return stack;
} }
public NBTTagCompound writeToNBT(NBTTagCompound nbt) public NBTTagCompound writeToNBT(NBTTagCompound nbt)
{ {
nbt.setString("FluidName", FluidRegistry.getFluidName(fluidID)); nbt.setString("FluidName", FluidRegistry.getFluidName(getFluid()));
nbt.setInteger("Amount", amount); nbt.setInteger("Amount", amount);
if (tag != null) if (tag != null)
@ -97,7 +113,12 @@ public class FluidStack
public final Fluid getFluid() public final Fluid getFluid()
{ {
return FluidRegistry.getFluid(fluidID); return fluidDelegate.get();
}
public final int getFluidID()
{
return FluidRegistry.getFluidID(getFluid());
} }
public String getLocalizedName() public String getLocalizedName()
@ -115,7 +136,7 @@ public class FluidStack
*/ */
public FluidStack copy() public FluidStack copy()
{ {
return new FluidStack(fluidID, amount, tag); return new FluidStack(getFluid(), amount, tag);
} }
/** /**
@ -127,7 +148,7 @@ public class FluidStack
*/ */
public boolean isFluidEqual(FluidStack other) public boolean isFluidEqual(FluidStack other)
{ {
return other != null && fluidID == other.fluidID && isFluidStackTagEqual(other); return other != null && getFluid() == other.getFluid() && isFluidStackTagEqual(other);
} }
private boolean isFluidStackTagEqual(FluidStack other) private boolean isFluidStackTagEqual(FluidStack other)
@ -192,7 +213,12 @@ public class FluidStack
@Override @Override
public final int hashCode() public final int hashCode()
{ {
return fluidID; int code = 1;
code = 31*code + getFluid().hashCode();
code = 31*code + amount;
if (tag != null)
code = 31*code + tag.hashCode();
return code;
} }
/** /**