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.util.IThreadListener;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StringUtils;
import net.minecraft.world.WorldSettings;
import net.minecraft.world.storage.SaveFormatOld;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
@ -991,9 +992,17 @@ public class FMLClientHandler implements IFMLSidedHandler
public void processWindowMessages()
{
// workaround for windows requiring messages being processed on the main thread
if(LWJGLUtil.getPlatform() == LWJGLUtil.PLATFORM_WINDOWS)
{
Display.processMessages();
}
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
// 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.util.Iterator;
import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ -131,6 +132,7 @@ public class SplashProgress
private static int barBorderColor;
private static int barColor;
private static int barBackgroundColor;
static final Semaphore mutex = new Semaphore(1);
private static String getString(String name, String def)
{
@ -352,7 +354,16 @@ public class SplashProgress
glEnd();
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();
// 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)
{
clearGL();

View file

@ -764,4 +764,8 @@ public class FMLCommonHandler
props.clear();
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);
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.registry.GameData;
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.GameRegistry.Type;
import net.minecraftforge.fml.common.toposort.ModSorter;
@ -524,8 +525,10 @@ public class Loader
return;
}
ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable());
ItemStackHolderInjector.INSTANCE.findHolders(discoverer.getASMTable());
modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir);
ObjectHolderRegistry.INSTANCE.applyObjectHolders();
ItemStackHolderInjector.INSTANCE.inject();
modController.transition(LoaderState.INITIALIZATION, false);
progressBar.step("Initializing Minecraft Engine");
}
@ -614,7 +617,7 @@ public class Loader
public String getFMLVersionString()
{
return String.format("%s.%s.%s.%s", major, minor, rev, build);
return "8.0.99.99";
}
public ClassLoader getModClassLoader()
@ -708,6 +711,7 @@ public class Loader
progressBar.step("Initializing mods Phase 3");
modController.transition(LoaderState.POSTINITIALIZATION, false);
modController.distributeStateMessage(FMLInterModComms.IMCEvent.class);
ItemStackHolderInjector.INSTANCE.inject();
modController.distributeStateMessage(LoaderState.POSTINITIALIZATION);
progressBar.step("Finishing up");
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);
step++;
this.message = message;
this.message = FMLCommonHandler.instance().stripSpecialChars(message);
FMLCommonHandler.instance().processWindowMessages();
}

View file

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

View file

@ -100,7 +100,7 @@ public class MarkerTransformer implements IClassTransformer
@Override
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; }
ClassNode classNode = new ClassNode();

View file

@ -34,7 +34,7 @@ public class SideTransformer implements IClassTransformer
@Override
public byte[] transform(String name, String transformedName, byte[] bytes)
{
if (bytes == null) { return null; }
if (bytes == null) { return null; }
ClassNode classNode = new ClassNode();
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.FurnaceRecipes;
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.util.ResourceLocation;
import net.minecraft.world.World;
@ -47,6 +51,8 @@ import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import org.apache.logging.log4j.Level;
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.Lists;
import com.google.common.collect.Maps;
@ -451,4 +457,70 @@ public class GameRegistry
*/
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
}
@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_;
if (flag1)
@@ -583,11 +584,6 @@
@@ -592,11 +593,6 @@
int j = this.field_78287_e[p_78263_1_] >>> 4;
int k = this.field_78287_e[p_78263_1_] & 15;

View file

@ -13,7 +13,7 @@ public class FMLForgePlugin implements IFMLLoadingPlugin
@Override
public String[] getASMTransformerClass()
{
return new String[0];
return new String[] { "net.minecraftforge.classloading.FluidIdTransformer" };
}
@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");
}
int allowedCount = ticketConstraints.containsKey(modId) ? ticketConstraints.get(modId) : defaultMaxCount;
int allowedCount = getMaxTicketLengthFor(modId);
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.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;
@ -60,7 +61,6 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
public static boolean removeErroringEntities = false;
public static boolean removeErroringTileEntities = false;
public static boolean disableStitchedFileSaving = false;
public static boolean forceDuplicateFluidBlockCrash = true;
public static boolean fullBoundingBoxLadders = false;
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 };
@ -68,6 +68,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
public static boolean shouldSortRecipies = true;
public static boolean disableVersionCheck = false;
public static int defaultSpawnFuzz = 20;
public static boolean defaultHasSpawnFuzz = true;
private static Configuration config;
@ -157,17 +158,6 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
shouldSortRecipies = prop.getBoolean(shouldSortRecipies);
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.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);
@ -225,6 +215,12 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
defaultSpawnFuzz = prop.getInt(20);
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);
if (config.hasChanged())
@ -301,6 +297,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
{
RecipeSorter.sortCraftManager();
}
FluidRegistry.validateFluidRegistry();
}
@Subscribe
@ -314,6 +311,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;
}
@ -321,6 +319,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
public void readData(SaveHandler handler, WorldInfo info, Map<String, NBTBase> propertyMap, NBTTagCompound tag)
{
DimensionManager.loadDimensionDataMap(tag.hasKey("DimensionData") ? tag.getCompoundTag("DimensionData") : null);
FluidRegistry.loadFluidDefaults(tag);
}
@Subscribe

View file

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

View file

@ -1,12 +1,18 @@
package net.minecraftforge.common.network;
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 com.google.common.collect.BiMap;
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 io.netty.buffer.ByteBuf;
@ -40,17 +46,23 @@ public abstract class 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
void toBytes(ByteBuf bytes)
{
Map<String, Integer> ids = FluidRegistry.getRegisteredFluidIDs();
Map<Fluid, Integer> ids = FluidRegistry.getRegisteredFluidIDsByFluid();
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());
}
for (Map.Entry<Fluid, Integer> entry : ids.entrySet())
{
String defaultName = FluidRegistry.getDefaultFluidName(entry.getKey());
ByteBufUtils.writeUTF8String(bytes, defaultName);
}
}
@Override
@ -60,7 +72,21 @@ public abstract class ForgeMessage {
for (int i = 0; i < listSize; i++) {
String fluidName = ByteBufUtils.readUTF8String(bytes);
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;
/**
* 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)
{
super(material);
@ -111,6 +118,7 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
this.densityDir = fluid.density > 0 ? -1 : 1;
fluid.setBlock(this);
this.definedFluid = fluid;
displacements.putAll(defaultDisplacements);
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.
*/
protected int temperature = 295;
protected int temperature = 300;
/**
* Viscosity ("thickness") of the fluid - completely arbitrary; negative values are not
* permissible.
*
* 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;
@ -117,17 +121,10 @@ public class Fluid
{
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
{
FMLLog.severe("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.");
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."));
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 + ". You may have duplicate Fluid Blocks as a result. It *may* be possible to configure your mods to avoid this.");
}
return this;
}
@ -320,19 +317,4 @@ public class Fluid
public int getColor(World world, BlockPos pos){ return getColor(); }
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.Sets;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
@ -51,7 +52,7 @@ public abstract class FluidContainerRegistry
code = 31*code + container.getItem().hashCode();
code = 31*code + container.getItemDamage();
if (fluid != null)
code = 31*code + fluid.fluidID;
code = 31*code + fluid.getFluid().hashCode();
return code;
}
@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 true;
if (fluid.fluidID != ck.fluid.fluidID) return false;
if (fluid.getFluid() != ck.fluid.getFluid()) return false;
return true;
}
}
@ -131,7 +132,7 @@ public abstract class FluidContainerRegistry
* FluidStack containing the type and amount of the fluid stored in the item.
* @param filledContainer
* 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)
{
@ -146,7 +147,7 @@ public abstract class FluidContainerRegistry
* Fluid type that is stored in the item.
* @param filledContainer
* 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)
{
@ -162,14 +163,19 @@ public abstract class FluidContainerRegistry
*
* @param data
* 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)
{
if (isFilledContainer(data.filledContainer))
if (isFilledContainer(data.filledContainer) || data.filledContainer == null)
{
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);
if (data.emptyContainer != null && data.emptyContainer != NULL_EMPTYCONTAINER)

View file

@ -1,19 +1,31 @@
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.base.Strings;
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 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.registry.RegistryDelegate;
/**
* Handles Fluid registrations. Fluids MUST be registered in order to function.
@ -25,10 +37,16 @@ public abstract class FluidRegistry
{
static int maxID = 0;
static HashMap<String, Fluid> fluids = Maps.newHashMap();
static BiMap<String, Integer> fluidIDs = HashBiMap.create();
static BiMap<String, Fluid> fluids = 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;
// 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") {
@Override
public String getLocalizedName() {
@ -57,41 +75,109 @@ 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<String, Integer> newfluidIDs)
public static void initFluidIDs(BiMap<Fluid, Integer> newfluidIDs, Set<String> defaultNames)
{
maxID = newfluidIDs.size();
fluidIDs.clear();
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
* 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)
{
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;
}
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));
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 fluidIDs.containsKey(fluid.getName());
return fluid != null && fluids.containsKey(fluid.getName());
}
public static boolean isFluidRegistered(String fluidName)
{
return fluidIDs.containsKey(fluidName);
return fluids.containsKey(fluidName);
}
public static Fluid getFluid(String fluidName)
@ -101,31 +187,42 @@ public abstract class FluidRegistry
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);
}
public static String getFluidName(FluidStack stack)
{
return getFluidName(stack.fluidID);
return fluidIDs.get(fluid);
}
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)
{
if (!fluidIDs.containsKey(fluidName))
if (!fluids.containsKey(fluidName))
{
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.
*/
@Deprecated //Change return type to <Fluid, Integer> in 1.8.3
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);
}
@ -177,4 +284,110 @@ public abstract class FluidRegistry
{
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;
import java.util.Locale;
import com.google.common.base.Strings;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.registry.RegistryDelegate;
/**
* ItemStack substitute for Fluids.
@ -18,25 +18,36 @@ import net.minecraft.nbt.NBTTagCompound;
*/
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 NBTTagCompound tag;
private RegistryDelegate<Fluid> fluidDelegate;
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.fluid = fluid;
}
public FluidStack(int fluidID, int amount)
public FluidStack(Fluid fluid, int amount, NBTTagCompound nbt)
{
this.fluidID = fluidID;
this.amount = amount;
}
public FluidStack(int fluidID, int amount, NBTTagCompound nbt)
{
this(fluidID, amount);
this(fluid, amount);
if (nbt != null)
{
@ -46,7 +57,21 @@ public class FluidStack
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;
}
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;
}
FluidStack stack = new FluidStack(FluidRegistry.getFluidID(fluidName), nbt.getInteger("Amount"));
FluidStack stack = new FluidStack(FluidRegistry.getFluid(fluidName), nbt.getInteger("Amount"));
if (nbt.hasKey("Tag"))
{
stack.tag = nbt.getCompoundTag("Tag");
}
else if (nbt.hasKey("extra"))
{
stack.tag = nbt.getCompoundTag("extra");
}
return stack;
}
public NBTTagCompound writeToNBT(NBTTagCompound nbt)
{
nbt.setString("FluidName", FluidRegistry.getFluidName(fluidID));
nbt.setString("FluidName", FluidRegistry.getFluidName(getFluid()));
nbt.setInteger("Amount", amount);
if (tag != null)
@ -97,7 +113,12 @@ public class FluidStack
public final Fluid getFluid()
{
return FluidRegistry.getFluid(fluidID);
return fluidDelegate.get();
}
public final int getFluidID()
{
return FluidRegistry.getFluidID(getFluid());
}
public String getLocalizedName()
@ -115,7 +136,7 @@ public class FluidStack
*/
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)
{
return other != null && fluidID == other.fluidID && isFluidStackTagEqual(other);
return other != null && getFluid() == other.getFluid() && isFluidStackTagEqual(other);
}
private boolean isFluidStackTagEqual(FluidStack other)
@ -192,7 +213,12 @@ public class FluidStack
@Override
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;
}
/**