diff --git a/src/main/java/cpw/mods/fml/client/FMLClientHandler.java b/src/main/java/cpw/mods/fml/client/FMLClientHandler.java index 949d92e..cc02906 100644 --- a/src/main/java/cpw/mods/fml/client/FMLClientHandler.java +++ b/src/main/java/cpw/mods/fml/client/FMLClientHandler.java @@ -716,7 +716,7 @@ } Map modListMap = modListBldr.build(); - serverDataTag.put(data, new ExtendedServerListData(type, FMLNetworkHandler.checkModList(modListMap, Side.CLIENT) == null, modListMap, !moddedClientAllowed)); + serverDataTag.put(data, new ExtendedServerListData(type, FMLNetworkHandler.checkModList(modListMap, Side.SERVER) == null, modListMap, !moddedClientAllowed)); } else { diff --git a/src/main/java/cpw/mods/fml/common/Loader.java b/src/main/java/cpw/mods/fml/common/Loader.java index 493fdf0..2e00909 100644 --- a/src/main/java/cpw/mods/fml/common/Loader.java +++ b/src/main/java/cpw/mods/fml/common/Loader.java @@ -62,6 +62,7 @@ import cpw.mods.fml.common.functions.ModIdFunction; import cpw.mods.fml.common.registry.GameData; import cpw.mods.fml.common.registry.GameRegistry.Type; +import cpw.mods.fml.common.registry.ObjectHolderRegistry; import cpw.mods.fml.common.toposort.ModSorter; import cpw.mods.fml.common.toposort.ModSortingException; import cpw.mods.fml.common.toposort.ModSortingException.SortingExceptionData; @@ -502,12 +503,14 @@ public void preinitializeMods() { + ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable()); if (!modController.isInState(LoaderState.PREINITIALIZATION)) { FMLLog.warning("There were errors previously. Not beginning mod initialization phase"); return; } modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir); + ObjectHolderRegistry.INSTANCE.applyObjectHolders(); modController.transition(LoaderState.INITIALIZATION, false); } diff --git a/src/main/java/cpw/mods/fml/common/asm/transformers/AccessTransformer.java b/src/main/java/cpw/mods/fml/common/asm/transformers/AccessTransformer.java index 2ecae1b..20e1a77 100644 --- a/src/main/java/cpw/mods/fml/common/asm/transformers/AccessTransformer.java +++ b/src/main/java/cpw/mods/fml/common/asm/transformers/AccessTransformer.java @@ -16,6 +16,8 @@ import static org.objectweb.asm.Opcodes.ACC_PRIVATE; import static org.objectweb.asm.Opcodes.ACC_PROTECTED; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -27,6 +29,7 @@ import java.io.IOException; import java.net.URL; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -36,8 +39,10 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import com.google.common.base.Charsets; @@ -206,11 +211,28 @@ } else { + List nowOverridable = Lists.newArrayList(); for (MethodNode n : classNode.methods) { if ((n.name.equals(m.name) && n.desc.equals(m.desc)) || m.name.equals("*")) { n.access = getFixedAccess(n.access, m); + + // constructors always use INVOKESPECIAL + if (!n.name.equals("")) + { + // if we changed from private to something else we need to replace all INVOKESPECIAL calls to this method with INVOKEVIRTUAL + // so that overridden methods will be called. Only need to scan this class, because obviously the method was private. + boolean wasPrivate = (m.oldAccess & ACC_PRIVATE) == ACC_PRIVATE; + boolean isNowPrivate = (m.newAccess & ACC_PRIVATE) == ACC_PRIVATE; + + if (wasPrivate && !isNowPrivate) + { + nowOverridable.add(n); + } + + } + if (DEBUG) { System.out.println(String.format("Method: %s.%s%s %s -> %s", name, n.name, n.desc, toBinary(m.oldAccess), toBinary(m.newAccess))); @@ -222,6 +244,8 @@ } } } + + replaceInvokeSpecial(classNode, nowOverridable); } } @@ -229,6 +253,29 @@ classNode.accept(writer); return writer.toByteArray(); } + + private void replaceInvokeSpecial(ClassNode clazz, List toReplace) + { + for (MethodNode method : clazz.methods) + { + for (Iterator it = method.instructions.iterator(); it.hasNext();) + { + AbstractInsnNode insn = it.next(); + if (insn.getOpcode() == INVOKESPECIAL) + { + MethodInsnNode mInsn = (MethodInsnNode) insn; + for (MethodNode n : toReplace) + { + if (n.name.equals(mInsn.name) && n.desc.equals(mInsn.desc)) + { + mInsn.setOpcode(INVOKEVIRTUAL); + break; + } + } + } + } + } + } private String toBinary(int num) { diff --git a/src/main/java/cpw/mods/fml/common/asm/transformers/deobf/FMLDeobfuscatingRemapper.java b/src/main/java/cpw/mods/fml/common/asm/transformers/deobf/FMLDeobfuscatingRemapper.java index 1f3b15a..f1ef462 100644 --- a/src/main/java/cpw/mods/fml/common/asm/transformers/deobf/FMLDeobfuscatingRemapper.java +++ b/src/main/java/cpw/mods/fml/common/asm/transformers/deobf/FMLDeobfuscatingRemapper.java @@ -53,7 +53,6 @@ public static final FMLDeobfuscatingRemapper INSTANCE = new FMLDeobfuscatingRemapper(); private BiMap classNameBiMap; - private BiMap mcpNameBiMap; private Map> rawFieldMaps; private Map> rawMethodMaps; @@ -71,7 +70,6 @@ private FMLDeobfuscatingRemapper() { classNameBiMap=ImmutableBiMap.of(); - mcpNameBiMap=ImmutableBiMap.of(); } public void setupLoadOnly(String deobfFileName, boolean loadAll) @@ -85,7 +83,6 @@ rawMethodMaps = Maps.newHashMap(); rawFieldMaps = Maps.newHashMap(); Builder builder = ImmutableBiMap.builder(); - Builder mcpBuilder = ImmutableBiMap.builder(); Splitter splitter = Splitter.on(CharMatcher.anyOf(": ")).omitEmptyStrings().trimResults(); for (String line : srgList) { @@ -94,7 +91,6 @@ if ("CL".equals(typ)) { parseClass(builder, parts); - parseMCPClass(mcpBuilder,parts); } else if ("MD".equals(typ) && loadAll) { @@ -106,7 +102,6 @@ } } classNameBiMap = builder.build(); - mcpNameBiMap = mcpBuilder.build(); } catch (IOException ioe) { @@ -128,7 +123,6 @@ rawMethodMaps = Maps.newHashMap(); rawFieldMaps = Maps.newHashMap(); Builder builder = ImmutableBiMap.builder(); - Builder mcpBuilder = ImmutableBiMap.builder(); Splitter splitter = Splitter.on(CharMatcher.anyOf(": ")).omitEmptyStrings().trimResults(); for (String line : srgList) { @@ -137,7 +131,6 @@ if ("CL".equals(typ)) { parseClass(builder, parts); - parseMCPClass(mcpBuilder,parts); } else if ("MD".equals(typ)) { @@ -149,13 +142,6 @@ } } classNameBiMap = builder.build(); - // Special case some mappings for modloader mods - mcpBuilder.put("BaseMod","net/minecraft/src/BaseMod"); - mcpBuilder.put("ModLoader","net/minecraft/src/ModLoader"); - mcpBuilder.put("EntityRendererProxy","net/minecraft/src/EntityRendererProxy"); - mcpBuilder.put("MLProp","net/minecraft/src/MLProp"); - mcpBuilder.put("TradeEntry","net/minecraft/src/TradeEntry"); - mcpNameBiMap = mcpBuilder.build(); } catch (IOException ioe) { @@ -167,8 +153,7 @@ public boolean isRemappedClass(String className) { - className = className.replace('.', '/'); - return classNameBiMap.containsKey(className) || mcpNameBiMap.containsKey(className) || (!classNameBiMap.isEmpty() && className.indexOf('/') == -1); + return !map(className).equals(className); } private void parseField(String[] parts) @@ -235,12 +220,6 @@ builder.put(parts[1],parts[2]); } - private void parseMCPClass(Builder builder, String[] parts) - { - int clIdx = parts[2].lastIndexOf('/'); - builder.put("net/minecraft/src/"+parts[2].substring(clIdx+1),parts[2]); - } - private void parseMethod(String[] parts) { String oldSrg = parts[1]; @@ -280,13 +259,12 @@ { return classNameBiMap.get(typeName); } - int dollarIdx = typeName.indexOf('$'); - String realType = dollarIdx > -1 ? typeName.substring(0, dollarIdx) : typeName; - String subType = dollarIdx > -1 ? typeName.substring(dollarIdx+1) : ""; - - String result = classNameBiMap.containsKey(realType) ? classNameBiMap.get(realType) : mcpNameBiMap.containsKey(realType) ? mcpNameBiMap.get(realType) : realType; - result = dollarIdx > -1 ? result+"$"+subType : result; - return result; + int dollarIdx = typeName.lastIndexOf('$'); + if (dollarIdx > -1) + { + return map(typeName.substring(0, dollarIdx)) + "$" + typeName.substring(dollarIdx + 1); + } + return typeName; } public String unmap(String typeName) @@ -300,14 +278,12 @@ { return classNameBiMap.inverse().get(typeName); } - int dollarIdx = typeName.indexOf('$'); - String realType = dollarIdx > -1 ? typeName.substring(0, dollarIdx) : typeName; - String subType = dollarIdx > -1 ? typeName.substring(dollarIdx+1) : ""; - - - String result = classNameBiMap.containsValue(realType) ? classNameBiMap.inverse().get(realType) : mcpNameBiMap.containsValue(realType) ? mcpNameBiMap.inverse().get(realType) : realType; - result = dollarIdx > -1 ? result+"$"+subType : result; - return result; + int dollarIdx = typeName.lastIndexOf('$'); + if (dollarIdx > -1) + { + return unmap(typeName.substring(0, dollarIdx)) + "$" + typeName.substring(dollarIdx + 1); + } + return typeName; } diff --git a/src/main/java/cpw/mods/fml/common/eventhandler/ListenerList.java b/src/main/java/cpw/mods/fml/common/eventhandler/ListenerList.java index 50aeeb7..bba1fb4 100644 --- a/src/main/java/cpw/mods/fml/common/eventhandler/ListenerList.java +++ b/src/main/java/cpw/mods/fml/common/eventhandler/ListenerList.java @@ -1,11 +1,12 @@ package cpw.mods.fml.common.eventhandler; import java.util.*; +import com.google.common.collect.ImmutableList; public class ListenerList { - private static ArrayList allLists = new ArrayList(); + private static ImmutableList allLists = ImmutableList.of(); private static int maxSize = 0; private ListenerList parent; @@ -13,17 +14,25 @@ public ListenerList() { - allLists.add(this); - resizeLists(maxSize); + this(null); } public ListenerList(ListenerList parent) { - allLists.add(this); + // parent needs to be set before resize ! this.parent = parent; + extendMasterList(this); resizeLists(maxSize); } + private synchronized static void extendMasterList(ListenerList inst) + { + ImmutableList.Builder builder = ImmutableList.builder(); + builder.addAll(allLists); + builder.add(inst); + allLists = builder.build(); + } + public static void resize(int max) { if (max <= maxSize) diff --git a/src/main/java/cpw/mods/fml/common/network/FMLOutboundHandler.java b/src/main/java/cpw/mods/fml/common/network/FMLOutboundHandler.java index 0f094f2..56be403 100644 --- a/src/main/java/cpw/mods/fml/common/network/FMLOutboundHandler.java +++ b/src/main/java/cpw/mods/fml/common/network/FMLOutboundHandler.java @@ -106,8 +106,8 @@ public List selectNetworks(Object args, ChannelHandlerContext context, FMLProxyPacket packet) { EntityPlayerMP player = (EntityPlayerMP) args; - NetworkDispatcher dispatcher = player.playerNetServerHandler.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(); - return ImmutableList.of(dispatcher); + NetworkDispatcher dispatcher = player == null ? null : player.playerNetServerHandler.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(); + return dispatcher == null ? ImmutableList.of() : ImmutableList.of(dispatcher); } }, /** @@ -129,7 +129,7 @@ for (EntityPlayerMP player : (List)FMLCommonHandler.instance().getMinecraftServerInstance().getConfigurationManager().playerEntityList) { NetworkDispatcher dispatcher = player.playerNetServerHandler.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(); - builder.add(dispatcher); + if (dispatcher != null) builder.add(dispatcher); } return builder.build(); } @@ -160,7 +160,8 @@ if (dimension == player.dimension) { NetworkDispatcher dispatcher = player.playerNetServerHandler.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(); - builder.add(dispatcher); + // Null dispatchers may exist for fake players - skip them + if (dispatcher != null) builder.add(dispatcher); } } return builder.build(); @@ -200,7 +201,7 @@ if (d4 * d4 + d5 * d5 + d6 * d6 < tp.range * tp.range) { NetworkDispatcher dispatcher = player.playerNetServerHandler.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(); - builder.add(dispatcher); + if (dispatcher != null) builder.add(dispatcher); } } } @@ -222,7 +223,7 @@ public List selectNetworks(Object args, ChannelHandlerContext context, FMLProxyPacket packet) { NetworkManager clientConnection = FMLCommonHandler.instance().getClientToServerNetworkManager(); - return clientConnection == null ? ImmutableList.of() : ImmutableList.of(clientConnection.channel().attr(NetworkDispatcher.FML_DISPATCHER).get()); + return clientConnection == null || clientConnection.channel().attr(NetworkDispatcher.FML_DISPATCHER).get() == null ? ImmutableList.of() : ImmutableList.of(clientConnection.channel().attr(NetworkDispatcher.FML_DISPATCHER).get()); } }; diff --git a/src/main/java/cpw/mods/fml/common/network/internal/FMLProxyPacket.java b/src/main/java/cpw/mods/fml/common/network/internal/FMLProxyPacket.java index 6e8d6d7..63701e3 100644 --- a/src/main/java/cpw/mods/fml/common/network/internal/FMLProxyPacket.java +++ b/src/main/java/cpw/mods/fml/common/network/internal/FMLProxyPacket.java @@ -11,6 +11,11 @@ import net.minecraft.network.play.client.C17PacketCustomPayload; import net.minecraft.network.play.server.S3FPacketCustomPayload; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.helpers.Integers; +import com.google.common.collect.ConcurrentHashMultiset; +import com.google.common.collect.Multiset; +import com.google.common.collect.Multiset.Entry; +import com.google.common.collect.Multisets; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.network.FMLNetworkException; import cpw.mods.fml.common.network.NetworkRegistry; @@ -23,7 +28,8 @@ private final ByteBuf payload; private INetHandler netHandler; private NetworkDispatcher dispatcher; - + private static Multiset badPackets = ConcurrentHashMultiset.create(); + private static int packetCountWarning = Integers.parseInt(System.getProperty("fml.badPacketCounter", "100"), 100); private FMLProxyPacket(byte[] payload, String channel) { this(Unpooled.wrappedBuffer(payload), channel); @@ -68,7 +74,21 @@ internalChannel.attr(NetworkRegistry.NET_HANDLER).set(this.netHandler); try { - internalChannel.writeInbound(this); + if (internalChannel.writeInbound(this)) + { + badPackets.add(this.channel); + if (badPackets.size() % packetCountWarning == 0) + { + FMLLog.severe("Detected ongoing potential memory leak. %d packets have leaked. Top offenders", badPackets.size()); + int i = 0; + for (Entry s : Multisets.copyHighestCountFirst(badPackets).entrySet()) + { + if (i++ > 10) break; + FMLLog.severe("\t %s : %d", s.getElement(), s.getCount()); + } + } + } + internalChannel.inboundMessages().clear(); } catch (FMLNetworkException ne) { diff --git a/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleChannelHandlerWrapper.java b/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleChannelHandlerWrapper.java index 79ef0fc..e934213 100644 --- a/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleChannelHandlerWrapper.java +++ b/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleChannelHandlerWrapper.java @@ -4,6 +4,7 @@ import net.minecraft.network.INetHandler; import com.google.common.base.Throwables; import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.network.FMLOutboundHandler; import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.relauncher.Side; import io.netty.channel.ChannelFutureListener; @@ -28,11 +29,12 @@ @Override protected void channelRead0(ChannelHandlerContext ctx, REQ msg) throws Exception { - INetHandler iNetHandler = ctx.attr(NetworkRegistry.NET_HANDLER).get(); + INetHandler iNetHandler = ctx.channel().attr(NetworkRegistry.NET_HANDLER).get(); MessageContext context = new MessageContext(iNetHandler, side); REPLY result = messageHandler.onMessage(msg, context); if (result != null) { + ctx.channel().attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.REPLY); ctx.writeAndFlush(result).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); } } diff --git a/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java b/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java index a00c2ec..2076168 100644 --- a/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java +++ b/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java @@ -330,7 +330,7 @@ if (name.isEmpty()) throw new IllegalArgumentException(String.format("Can't use an empty name for the registry, object %s.", thing)); if (name.indexOf(':') == -1) throw new IllegalArgumentException(String.format("Can't add the name (%s) without a prefix, object %s", name, thing)); if (thing == null) throw new NullPointerException(String.format("Can't add null-object to the registry, name %s.", name)); - if (name.equals(optionalDefaultName)) + if (name.equals(optionalDefaultName) && this.optionalDefaultObject == null) { this.optionalDefaultObject = thing; } @@ -420,4 +420,9 @@ underlyingIntegerMap.func_148746_a(thing, id); // obj <-> id super.putObject(name, thing); // name <-> obj } + + public I getDefaultValue() + { + return optionalDefaultObject; + } } \ No newline at end of file diff --git a/src/main/java/cpw/mods/fml/common/registry/GameData.java b/src/main/java/cpw/mods/fml/common/registry/GameData.java index c412ee1..2b317a4 100644 --- a/src/main/java/cpw/mods/fml/common/registry/GameData.java +++ b/src/main/java/cpw/mods/fml/common/registry/GameData.java @@ -530,6 +530,8 @@ getMain().iBlockRegistry.dump(); getMain().iItemRegistry.dump(); Loader.instance().fireRemapEvent(remaps); + // The id map changed, ensure we apply object holders + ObjectHolderRegistry.INSTANCE.applyObjectHolders(); return ImmutableList.of(); } @@ -664,6 +666,8 @@ getMain().set(frozen); } + // the id mapping has reverted, ensure we sync up the object holders + ObjectHolderRegistry.INSTANCE.applyObjectHolders(); } protected static boolean isFrozen(FMLControlledNamespacedRegistry registry) diff --git a/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java b/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java index 6a9e604..1f59c99 100644 --- a/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java +++ b/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java @@ -12,6 +12,10 @@ package cpw.mods.fml.common.registry; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collections; @@ -424,4 +428,24 @@ return GameData.getUniqueName(item); } + + + /** + * This will cause runtime injection of public static final fields to occur at various points + * where mod blocks and items could be subject to change. This allows for dynamic + * substitution to occur. + * + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE, ElementType.FIELD}) + public @interface ObjectHolder { + /** + * If used on a class, this represents a modid only. + * If used on a field, it represents a name, which can be abbreviated or complete. + * Abbreviated names derive their modid from an enclosing ObjectHolder at the class level. + * + * @return either a modid or a name based on the rules above + */ + String value(); + } } \ No newline at end of file diff --git a/src/main/java/cpw/mods/fml/common/registry/ObjectHolderRef.java b/src/main/java/cpw/mods/fml/common/registry/ObjectHolderRef.java new file mode 100644 index 0000000..ef82e9c --- /dev/null +++ b/src/main/java/cpw/mods/fml/common/registry/ObjectHolderRef.java @@ -0,0 +1,132 @@ +package cpw.mods.fml.common.registry; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import org.apache.logging.log4j.Level; +import com.google.common.base.Throwables; +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.registry.GameRegistry.ObjectHolder; +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; + + +/** + * Internal class used in tracking {@link ObjectHolder} references + * + * @author cpw + * + */ +class ObjectHolderRef { + private Field field; + private String injectedObject; + private boolean isBlock; + private boolean isItem; + + + ObjectHolderRef(Field field, String injectedObject, boolean extractFromExistingValues) + { + this.field = field; + this.isBlock = Block.class.isAssignableFrom(field.getType()); + this.isItem = Item.class.isAssignableFrom(field.getType()); + if (extractFromExistingValues) + { + try + { + Object existing = field.get(null); + // nothing is ever allowed to replace AIR + if (existing == null || existing == GameData.getBlockRegistry().getDefaultValue()) + { + this.injectedObject = null; + this.field = null; + this.isBlock = false; + this.isItem = false; + return; + } + else + { + this.injectedObject = isBlock ? GameData.getBlockRegistry().getNameForObject(existing) : + isItem ? GameData.getItemRegistry().getNameForObject(existing) : null; + } + } catch (Exception e) + { + throw Throwables.propagate(e); + } + } + else + { + this.injectedObject = injectedObject; + } + + if (this.injectedObject == null || !isValid()) + { + throw new IllegalStateException("The ObjectHolder annotation cannot apply to a field that is not an Item or Block"); + } + 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 boolean isValid() + { + return isBlock || isItem; + } + public void apply() + { + Object thing; + if (isBlock) + { + thing = GameData.getBlockRegistry().getObject(injectedObject); + if (thing == Blocks.air) + { + thing = null; + } + } + else if (isItem) + { + thing = GameData.getItemRegistry().getObject(injectedObject); + } + else + { + thing = null; + } + + if (thing == null) + { + FMLLog.warning("Unable to lookup %s for %s. Is there something wrong with the registry?", injectedObject, field); + return; + } + try + { + Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false); + fieldAccessorSet.invoke(fieldAccessor, null, thing); + } + catch (Exception e) + { + FMLLog.log(Level.WARN, e, "Unable to set %s with value %s (%s)", this.field, thing, this.injectedObject); + } + } +} \ No newline at end of file diff --git a/src/main/java/cpw/mods/fml/common/registry/ObjectHolderRegistry.java b/src/main/java/cpw/mods/fml/common/registry/ObjectHolderRegistry.java new file mode 100644 index 0000000..bd9e922 --- /dev/null +++ b/src/main/java/cpw/mods/fml/common/registry/ObjectHolderRegistry.java @@ -0,0 +1,141 @@ +package cpw.mods.fml.common.registry; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Map; +import java.util.Set; +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.discovery.ASMDataTable; +import cpw.mods.fml.common.discovery.ASMDataTable.ASMData; +import cpw.mods.fml.common.registry.GameRegistry.ObjectHolder; + +/** + * Internal registry for tracking {@link ObjectHolder} references + * @author cpw + * + */ +public enum ObjectHolderRegistry { + INSTANCE; + private List objectHolders = Lists.newArrayList(); + + public void findObjectHolders(ASMDataTable table) + { + FMLLog.info("Processing ObjectHolder annotations"); + Set allObjectHolders = table.getAll(GameRegistry.ObjectHolder.class.getName()); + Map classModIds = Maps.newHashMap(); + Map> classCache = Maps.newHashMap(); + for (ASMData data : allObjectHolders) + { + String className = data.getClassName(); + String annotationTarget = data.getObjectName(); + String value = (String) data.getAnnotationInfo().get("value"); + boolean isClass = className.equals(annotationTarget); + if (isClass) + { + scanTarget(classModIds, classCache, className, annotationTarget, value, isClass, false); + } + } + // double pass - get all the class level annotations first, then the field level annotations + for (ASMData data : allObjectHolders) + { + String className = data.getClassName(); + String annotationTarget = data.getObjectName(); + String value = (String) data.getAnnotationInfo().get("value"); + boolean isClass = className.equals(annotationTarget); + if (!isClass) + { + scanTarget(classModIds, classCache, className, annotationTarget, value, isClass, false); + } + } + scanTarget(classModIds, classCache, "net.minecraft.init.Blocks", null, "minecraft", true, true); + scanTarget(classModIds, classCache, "net.minecraft.init.Items", null, "minecraft", true, true); + FMLLog.info("Found %d ObjectHolder annotations", objectHolders.size()); + } + + private void scanTarget(Map classModIds, Map> classCache, String className, String annotationTarget, String value, boolean isClass, boolean extractFromValue) + { + 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); + } + } + if (isClass) + { + scanClassForFields(classModIds, className, value, clazz, extractFromValue); + } + else + { + if (value.indexOf(':') == -1) + { + String prefix = classModIds.get(className); + if (prefix == null) + { + FMLLog.warning("Found an unqualified ObjectHolder annotation (%s) without a modid context at %s.%s, ignoring", value, className, annotationTarget); + throw new IllegalStateException("Unqualified reference to ObjectHolder"); + } + value = prefix + ":" + value; + } + try + { + Field f = clazz.getField(annotationTarget); + addHolderReference(new ObjectHolderRef(f, value, extractFromValue)); + } + catch (Exception ex) + { + // unpossible? + throw Throwables.propagate(ex); + } + } + } + + private void scanClassForFields(Map classModIds, String className, String value, Class clazz, boolean extractFromExistingValues) + { + classModIds.put(className, value); + for (Field f : clazz.getFields()) + { + int mods = f.getModifiers(); + boolean isMatch = Modifier.isPublic(mods) && Modifier.isStatic(mods) && Modifier.isFinal(mods); + if (!isMatch || f.isAnnotationPresent(ObjectHolder.class)) + { + continue; + } + addHolderReference(new ObjectHolderRef(f, value + ":"+ f.getName(), extractFromExistingValues)); + } + } + + private void addHolderReference(ObjectHolderRef ref) + { + if (ref.isValid()) + { + objectHolders.add(ref); + } + } + + public void applyObjectHolders() + { + FMLLog.info("Applying holder lookups"); + for (ObjectHolderRef ohr : objectHolders) + { + ohr.apply(); + } + FMLLog.info("Holder lookups applied"); + } + +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/block/Block.java b/src/main/java/net/minecraft/block/Block.java index 4b099a6..7c8f05f 100644 --- a/src/main/java/net/minecraft/block/Block.java +++ b/src/main/java/net/minecraft/block/Block.java @@ -667,6 +667,11 @@ { if (!p_149642_1_.isRemote && p_149642_1_.getGameRules().getGameRuleBooleanValue("doTileDrops")) { + if (captureDrops.get()) + { + capturedDrops.get().add(p_149642_5_); + return; + } float f = 0.7F; double d0 = (double)(p_149642_1_.rand.nextFloat() * f) + (double)(1.0F - f) * 0.5D; double d1 = (double)(p_149642_1_.rand.nextFloat() * f) + (double)(1.0F - f) * 0.5D; @@ -1149,7 +1154,8 @@ } /* ======================================== FORGE START =====================================*/ - private ThreadLocal harvesters = new ThreadLocal(); + //For ForgeInternal use Only! + protected ThreadLocal harvesters = new ThreadLocal(); private ThreadLocal silk_check_meta = new ThreadLocal(); /** * Get a light value for the block at the specified coordinates, normal ranges are between 0 and 15 @@ -1881,7 +1887,7 @@ case Nether: return this == Blocks.soul_sand; case Crop: return this == Blocks.farmland; case Cave: return isSideSolid(world, x, y, z, UP); - case Plains: return this == Blocks.grass || this == Blocks.dirt; + case Plains: return this == Blocks.grass || this == Blocks.dirt || this == Blocks.farmland; case Water: return world.getBlock(x, y, z).getMaterial() == Material.water && world.getBlockMetadata(x, y, z) == 0; case Beach: boolean isBeach = this == Blocks.grass || this == Blocks.dirt || this == Blocks.sand; @@ -2136,7 +2142,7 @@ * @param level Harvest level: * Wood: 0 * Stone: 1 - * Iton: 2 + * Iron: 2 * Diamond: 3 * Gold: 0 */ @@ -2155,7 +2161,7 @@ * @param level Harvest level: * Wood: 0 * Stone: 1 - * Iton: 2 + * Iron: 2 * Diamond: 3 * Gold: 0 * @param metadata The specific metadata to set @@ -2205,6 +2211,31 @@ if (harvestTool[metadata] == null) return false; return harvestTool[metadata].equals(type); } + + + // For Inernal use only to capture droped items inside getDrops + protected ThreadLocal captureDrops = new ThreadLocal() + { + @Override protected Boolean initialValue() { return false; } + }; + protected ThreadLocal> capturedDrops = new ThreadLocal>() + { + @Override protected List initialValue() { return new ArrayList(); } + }; + protected List captureDrops(boolean start) + { + if (start) + { + captureDrops.set(true); + capturedDrops.get().clear(); + return null; + } + else + { + captureDrops.set(false); + return capturedDrops.get(); + } + } /* ========================================= FORGE END ======================================*/ public static class SoundType diff --git a/src/main/java/net/minecraft/block/BlockDeadBush.java b/src/main/java/net/minecraft/block/BlockDeadBush.java index 3cfb81f..cd6ef30 100644 --- a/src/main/java/net/minecraft/block/BlockDeadBush.java +++ b/src/main/java/net/minecraft/block/BlockDeadBush.java @@ -1,5 +1,7 @@ package net.minecraft.block; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Random; import net.minecraft.block.material.Material; import net.minecraft.entity.player.EntityPlayer; @@ -8,9 +10,11 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.stats.StatList; +import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.common.IShearable; -public class BlockDeadBush extends BlockBush +public class BlockDeadBush extends BlockBush implements IShearable { private static final String __OBFID = "CL_00000224"; @@ -33,14 +37,15 @@ public void harvestBlock(World p_149636_1_, EntityPlayer p_149636_2_, int p_149636_3_, int p_149636_4_, int p_149636_5_, int p_149636_6_) { - if (!p_149636_1_.isRemote && p_149636_2_.getCurrentEquippedItem() != null && p_149636_2_.getCurrentEquippedItem().getItem() == Items.shears) - { - p_149636_2_.addStat(StatList.mineBlockStatArray[Block.getIdFromBlock(this)], 1); - this.dropBlockAsItem(p_149636_1_, p_149636_3_, p_149636_4_, p_149636_5_, new ItemStack(Blocks.deadbush, 1, p_149636_6_)); - } - else { super.harvestBlock(p_149636_1_, p_149636_2_, p_149636_3_, p_149636_4_, p_149636_5_, p_149636_6_); } } + + @Override public boolean isShearable(ItemStack item, IBlockAccess world, int x, int y, int z) { return true; } + @Override + public ArrayList onSheared(ItemStack item, IBlockAccess world, int x, int y, int z, int fortune) + { + return new ArrayList(Arrays.asList(new ItemStack(Blocks.deadbush, 1, world.getBlockMetadata(x, y, z)))); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/block/BlockDoublePlant.java b/src/main/java/net/minecraft/block/BlockDoublePlant.java index 27876b8..c3b02eb 100644 --- a/src/main/java/net/minecraft/block/BlockDoublePlant.java +++ b/src/main/java/net/minecraft/block/BlockDoublePlant.java @@ -2,6 +2,7 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import java.util.ArrayList; import java.util.List; import java.util.Random; import net.minecraft.block.material.Material; @@ -18,8 +19,9 @@ import net.minecraft.util.MathHelper; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.common.IShearable; -public class BlockDoublePlant extends BlockBush implements IGrowable +public class BlockDoublePlant extends BlockBush implements IGrowable, IShearable { public static final String[] field_149892_a = new String[] {"sunflower", "syringa", "grass", "fern", "rose", "paeonia"}; @SideOnly(Side.CLIENT) @@ -263,4 +265,22 @@ int l = this.func_149885_e(p_149853_1_, p_149853_3_, p_149853_4_, p_149853_5_); this.dropBlockAsItem(p_149853_1_, p_149853_3_, p_149853_4_, p_149853_5_, new ItemStack(this, 1, l)); } + + @Override + public boolean isShearable(ItemStack item, IBlockAccess world, int x, int y, int z) + { + int metadata = world.getBlockMetadata(x, y, z); + int type = func_149890_d(metadata); + return func_149887_c(metadata) && (type == 3 || type == 4); + } + + @Override + public ArrayList onSheared(ItemStack item, IBlockAccess world, int x, int y, int z, int fortune) + { + ArrayList ret = new ArrayList(); + int type = func_149890_d(world.getBlockMetadata(x, y, z)); + if (type == 3 || type == 2) + ret.add(new ItemStack(Blocks.tallgrass, 2, type == 3 ? 2 : 1)); + return ret; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/block/BlockFlowerPot.java b/src/main/java/net/minecraft/block/BlockFlowerPot.java index c2b666c..9397a5e 100644 --- a/src/main/java/net/minecraft/block/BlockFlowerPot.java +++ b/src/main/java/net/minecraft/block/BlockFlowerPot.java @@ -2,6 +2,7 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import java.util.ArrayList; import java.util.Random; import net.minecraft.block.material.Material; import net.minecraft.entity.player.EntityPlayer; @@ -139,12 +140,6 @@ public void dropBlockAsItemWithChance(World p_149690_1_, int p_149690_2_, int p_149690_3_, int p_149690_4_, int p_149690_5_, float p_149690_6_, int p_149690_7_) { super.dropBlockAsItemWithChance(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, p_149690_5_, p_149690_6_, p_149690_7_); - TileEntityFlowerPot tileentityflowerpot = this.func_149929_e(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_); - - if (tileentityflowerpot != null && tileentityflowerpot.getFlowerPotItem() != null) - { - this.dropBlockAsItem(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, new ItemStack(tileentityflowerpot.getFlowerPotItem(), 1, tileentityflowerpot.getFlowerPotData())); - } } public void breakBlock(World p_149749_1_, int p_149749_2_, int p_149749_3_, int p_149749_4_, Block p_149749_5_, int p_149749_6_) @@ -242,4 +237,14 @@ return new TileEntityFlowerPot(Item.getItemFromBlock((Block)object), b0); } + + @Override + public ArrayList getDrops(World world, int x, int y, int z, int metadata, int fortune) + { + ArrayList ret = super.getDrops(world, x, y, z, metadata, fortune); + TileEntityFlowerPot te = this.func_149929_e(world, x, y, z); + if (te != null && te.getFlowerPotItem() != null) + ret.add(new ItemStack(te.getFlowerPotItem(), 1, te.getFlowerPotData())); + return ret; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/block/BlockIce.java b/src/main/java/net/minecraft/block/BlockIce.java index 0f40507..f43691b 100644 --- a/src/main/java/net/minecraft/block/BlockIce.java +++ b/src/main/java/net/minecraft/block/BlockIce.java @@ -2,6 +2,7 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import java.util.ArrayList; import java.util.Random; import net.minecraft.block.material.Material; import net.minecraft.creativetab.CreativeTabs; @@ -13,6 +14,7 @@ import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; +import net.minecraftforge.event.ForgeEventFactory; public class BlockIce extends BlockBreakable { @@ -45,12 +47,14 @@ if (this.canSilkHarvest(p_149636_1_, p_149636_2_, p_149636_3_, p_149636_4_, p_149636_5_, p_149636_6_) && EnchantmentHelper.getSilkTouchModifier(p_149636_2_)) { + ArrayList items = new ArrayList(); ItemStack itemstack = this.createStackedBlock(p_149636_6_); - if (itemstack != null) - { - this.dropBlockAsItem(p_149636_1_, p_149636_3_, p_149636_4_, p_149636_5_, itemstack); - } + if (itemstack != null) items.add(itemstack); + + ForgeEventFactory.fireBlockHarvesting(items, p_149636_1_, this, p_149636_3_, p_149636_4_, p_149636_5_, p_149636_6_, 0, 1.0f, true, p_149636_2_); + for (ItemStack is : items) + this.dropBlockAsItem(p_149636_1_, p_149636_3_, p_149636_4_, p_149636_5_, is); } else { @@ -61,7 +65,9 @@ } int i1 = EnchantmentHelper.getFortuneModifier(p_149636_2_); + harvesters.set(p_149636_2_); this.dropBlockAsItem(p_149636_1_, p_149636_3_, p_149636_4_, p_149636_5_, p_149636_6_, i1); + harvesters.set(null); Material material = p_149636_1_.getBlock(p_149636_3_, p_149636_4_ - 1, p_149636_5_).getMaterial(); if (material.blocksMovement() || material.isLiquid()) diff --git a/src/main/java/net/minecraft/block/BlockLeaves.java b/src/main/java/net/minecraft/block/BlockLeaves.java index fdf36a1..5bf7d79 100644 --- a/src/main/java/net/minecraft/block/BlockLeaves.java +++ b/src/main/java/net/minecraft/block/BlockLeaves.java @@ -238,40 +238,7 @@ public void dropBlockAsItemWithChance(World p_149690_1_, int p_149690_2_, int p_149690_3_, int p_149690_4_, int p_149690_5_, float p_149690_6_, int p_149690_7_) { - if (!p_149690_1_.isRemote) - { - int j1 = this.func_150123_b(p_149690_5_); - - if (p_149690_7_ > 0) - { - j1 -= 2 << p_149690_7_; - - if (j1 < 10) - { - j1 = 10; - } - } - - if (p_149690_1_.rand.nextInt(j1) == 0) - { - Item item = this.getItemDropped(p_149690_5_, p_149690_1_.rand, p_149690_7_); - this.dropBlockAsItem(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, new ItemStack(item, 1, this.damageDropped(p_149690_5_))); - } - - j1 = 200; - - if (p_149690_7_ > 0) - { - j1 -= 10 << p_149690_7_; - - if (j1 < 40) - { - j1 = 40; - } - } - - this.func_150124_c(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, p_149690_5_, j1); - } + super.dropBlockAsItemWithChance(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, p_149690_5_, 1.0f, p_149690_7_); } protected void func_150124_c(World p_150124_1_, int p_150124_2_, int p_150124_3_, int p_150124_4_, int p_150124_5_, int p_150124_6_) {} @@ -348,4 +315,33 @@ { return true; } + + + @Override + public ArrayList getDrops(World world, int x, int y, int z, int metadata, int fortune) + { + ArrayList ret = new ArrayList(); + int chance = this.func_150123_b(metadata); + + if (fortune > 0) + { + chance -= 2 << fortune; + if (chance < 10) chance = 10; + } + + if (world.rand.nextInt(chance) == 0) + ret.add(new ItemStack(this.getItemDropped(metadata, world.rand, fortune), 1, this.damageDropped(metadata))); + + chance = 200; + if (fortune > 0) + { + chance -= 10 << fortune; + if (chance < 40) chance = 40; + } + + this.captureDrops(true); + this.func_150124_c(world, x, y, z, metadata, chance); // Dammet mojang + ret.addAll(this.captureDrops(false)); + return ret; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/block/BlockNetherWart.java b/src/main/java/net/minecraft/block/BlockNetherWart.java index ba0c9d5..1878a22 100644 --- a/src/main/java/net/minecraft/block/BlockNetherWart.java +++ b/src/main/java/net/minecraft/block/BlockNetherWart.java @@ -62,6 +62,7 @@ return 6; } + @SuppressWarnings("unused") public void dropBlockAsItemWithChance(World p_149690_1_, int p_149690_2_, int p_149690_3_, int p_149690_4_, int p_149690_5_, float p_149690_6_, int p_149690_7_) { super.dropBlockAsItemWithChance(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, p_149690_5_, p_149690_6_, p_149690_7_); diff --git a/src/main/java/net/minecraft/block/BlockNote.java b/src/main/java/net/minecraft/block/BlockNote.java index 2154c6e..d4f6b0b 100644 --- a/src/main/java/net/minecraft/block/BlockNote.java +++ b/src/main/java/net/minecraft/block/BlockNote.java @@ -45,7 +45,9 @@ if (tileentitynote != null) { + int old = tileentitynote.note; tileentitynote.changePitch(); + if (old == tileentitynote.note) return false; tileentitynote.triggerNote(p_149727_1_, p_149727_2_, p_149727_3_, p_149727_4_); } @@ -73,6 +75,11 @@ public boolean onBlockEventReceived(World p_149696_1_, int p_149696_2_, int p_149696_3_, int p_149696_4_, int p_149696_5_, int p_149696_6_) { + int meta = p_149696_1_.getBlockMetadata(p_149696_2_, p_149696_3_, p_149696_4_); + net.minecraftforge.event.world.NoteBlockEvent.Play e = new net.minecraftforge.event.world.NoteBlockEvent.Play(p_149696_1_, p_149696_2_, p_149696_3_, p_149696_4_, meta, p_149696_6_, p_149696_5_); + if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(e)) return false; + p_149696_5_ = e.instrument.ordinal(); + p_149696_6_ = e.getVanillaNoteId(); float f = (float)Math.pow(2.0D, (double)(p_149696_6_ - 12) / 12.0D); String s = "harp"; diff --git a/src/main/java/net/minecraft/block/BlockPistonMoving.java b/src/main/java/net/minecraft/block/BlockPistonMoving.java index d1152f4..89b2c69 100644 --- a/src/main/java/net/minecraft/block/BlockPistonMoving.java +++ b/src/main/java/net/minecraft/block/BlockPistonMoving.java @@ -2,11 +2,13 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import java.util.ArrayList; import java.util.Random; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityPiston; import net.minecraft.util.AxisAlignedBB; @@ -90,15 +92,7 @@ public void dropBlockAsItemWithChance(World p_149690_1_, int p_149690_2_, int p_149690_3_, int p_149690_4_, int p_149690_5_, float p_149690_6_, int p_149690_7_) { - if (!p_149690_1_.isRemote) - { - TileEntityPiston tileentitypiston = this.func_149963_e(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_); - - if (tileentitypiston != null) - { - tileentitypiston.getStoredBlockID().dropBlockAsItem(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, tileentitypiston.getBlockMetadata(), 0); - } - } + super.dropBlockAsItemWithChance(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, p_149690_5_, p_149690_6_, p_149690_7_); } public void onNeighborBlockChange(World p_149695_1_, int p_149695_2_, int p_149695_3_, int p_149695_4_, Block p_149695_5_) @@ -231,4 +225,13 @@ { this.blockIcon = p_149651_1_.registerIcon("piston_top_normal"); } + + @Override + public ArrayList getDrops(World world, int x, int y, int z, int metadata, int fortune) + { + TileEntityPiston te = this.func_149963_e(world, x, y, z); + if (te != null) + return te.getStoredBlockID().getDrops(world, x, y, z, te.getBlockMetadata(), 0); + return new ArrayList(); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/block/BlockPotato.java b/src/main/java/net/minecraft/block/BlockPotato.java index 9f9a680..e2a1325 100644 --- a/src/main/java/net/minecraft/block/BlockPotato.java +++ b/src/main/java/net/minecraft/block/BlockPotato.java @@ -1,5 +1,6 @@ package net.minecraft.block; +import java.util.ArrayList; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.renderer.texture.IIconRegister; @@ -46,14 +47,15 @@ public void dropBlockAsItemWithChance(World p_149690_1_, int p_149690_2_, int p_149690_3_, int p_149690_4_, int p_149690_5_, float p_149690_6_, int p_149690_7_) { super.dropBlockAsItemWithChance(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, p_149690_5_, p_149690_6_, p_149690_7_); + } - if (!p_149690_1_.isRemote) - { - if (p_149690_5_ >= 7 && p_149690_1_.rand.nextInt(50) == 0) - { - this.dropBlockAsItem(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, new ItemStack(Items.poisonous_potato)); - } - } + @Override + public ArrayList getDrops(World world, int x, int y, int z, int metadata, int fortune) + { + ArrayList ret = super.getDrops(world, x, y, z, metadata, fortune); + if (metadata >= 7 && world.rand.nextInt(50) == 0) + ret.add(new ItemStack(Items.poisonous_potato)); + return ret; } @SideOnly(Side.CLIENT) diff --git a/src/main/java/net/minecraft/block/BlockStem.java b/src/main/java/net/minecraft/block/BlockStem.java index 980aa69..53fd7d0 100644 --- a/src/main/java/net/minecraft/block/BlockStem.java +++ b/src/main/java/net/minecraft/block/BlockStem.java @@ -213,6 +213,7 @@ return l < 7 ? -1 : (p_149873_1_.getBlock(p_149873_2_ - 1, p_149873_3_, p_149873_4_) == this.field_149877_a ? 0 : (p_149873_1_.getBlock(p_149873_2_ + 1, p_149873_3_, p_149873_4_) == this.field_149877_a ? 1 : (p_149873_1_.getBlock(p_149873_2_, p_149873_3_, p_149873_4_ - 1) == this.field_149877_a ? 2 : (p_149873_1_.getBlock(p_149873_2_, p_149873_3_, p_149873_4_ + 1) == this.field_149877_a ? 3 : -1)))); } + @SuppressWarnings("unused") public void dropBlockAsItemWithChance(World p_149690_1_, int p_149690_2_, int p_149690_3_, int p_149690_4_, int p_149690_5_, float p_149690_6_, int p_149690_7_) { super.dropBlockAsItemWithChance(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, p_149690_5_, p_149690_6_, p_149690_7_); @@ -297,7 +298,8 @@ for (int i = 0; item != null && i < 3; i++) { - ret.add(new ItemStack(item)); + if (world.rand.nextInt(15) <= meta) + ret.add(new ItemStack(item)); } return ret; diff --git a/src/main/java/net/minecraft/block/BlockTallGrass.java b/src/main/java/net/minecraft/block/BlockTallGrass.java index 351d197..565ec67 100644 --- a/src/main/java/net/minecraft/block/BlockTallGrass.java +++ b/src/main/java/net/minecraft/block/BlockTallGrass.java @@ -56,7 +56,7 @@ public boolean canBlockStay(World p_149718_1_, int p_149718_2_, int p_149718_3_, int p_149718_4_) { - return this.canPlaceBlockOn(p_149718_1_.getBlock(p_149718_2_, p_149718_3_ - 1, p_149718_4_)); + return super.canBlockStay(p_149718_1_, p_149718_2_, p_149718_3_, p_149718_4_); } @SideOnly(Side.CLIENT) diff --git a/src/main/java/net/minecraft/client/Minecraft.java b/src/main/java/net/minecraft/client/Minecraft.java index ea23832..2c6b3db 100644 --- a/src/main/java/net/minecraft/client/Minecraft.java +++ b/src/main/java/net/minecraft/client/Minecraft.java @@ -1396,7 +1396,7 @@ { int l = itemstack != null ? itemstack.stackSize : 0; - boolean result = !ForgeEventFactory.onPlayerInteract(thePlayer, Action.RIGHT_CLICK_BLOCK, i, j, k, this.objectMouseOver.sideHit).isCanceled(); + boolean result = !ForgeEventFactory.onPlayerInteract(thePlayer, Action.RIGHT_CLICK_BLOCK, i, j, k, this.objectMouseOver.sideHit, this.theWorld).isCanceled(); if (result && this.playerController.onPlayerRightClick(this.thePlayer, this.theWorld, itemstack, i, j, k, this.objectMouseOver.sideHit, this.objectMouseOver.hitVec)) { flag = false; @@ -1424,7 +1424,7 @@ { ItemStack itemstack1 = this.thePlayer.inventory.getCurrentItem(); - boolean result = !ForgeEventFactory.onPlayerInteract(thePlayer, Action.RIGHT_CLICK_AIR, 0, 0, 0, -1).isCanceled(); + boolean result = !ForgeEventFactory.onPlayerInteract(thePlayer, Action.RIGHT_CLICK_AIR, 0, 0, 0, -1, this.theWorld).isCanceled(); if (result && itemstack1 != null && this.playerController.sendUseItem(this.thePlayer, this.theWorld, itemstack1)) { this.entityRenderer.itemRenderer.resetEquippedProgress2(); @@ -2118,7 +2118,7 @@ this.theIntegratedServer.initiateShutdown(); if (loadingScreen != null) { - this.loadingScreen.resetProgresAndWorkingMessage("Shutting down internal server..."); + this.loadingScreen.resetProgresAndWorkingMessage(I18n.format("forge.client.shutdown.internal")); } while (!theIntegratedServer.isServerStopped()) { diff --git a/src/main/java/net/minecraft/client/gui/GuiScreen.java b/src/main/java/net/minecraft/client/gui/GuiScreen.java index 3771c59..68f5045 100644 --- a/src/main/java/net/minecraft/client/gui/GuiScreen.java +++ b/src/main/java/net/minecraft/client/gui/GuiScreen.java @@ -116,7 +116,6 @@ } FontRenderer font = p_146285_1_.getItem().getFontRenderer(p_146285_1_); - this.func_146283_a(list, p_146285_2_, p_146285_3_); drawHoveringText(list, p_146285_2_, p_146285_3_, (font == null ? fontRendererObj : font)); } diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiContainer.java b/src/main/java/net/minecraft/client/gui/inventory/GuiContainer.java index 3519702..ae757df 100644 --- a/src/main/java/net/minecraft/client/gui/inventory/GuiContainer.java +++ b/src/main/java/net/minecraft/client/gui/inventory/GuiContainer.java @@ -466,6 +466,7 @@ protected void mouseMovedOrUp(int p_146286_1_, int p_146286_2_, int p_146286_3_) { + super.mouseMovedOrUp(p_146286_1_, p_146286_2_, p_146286_3_); //Forge, Call parent to release buttons Slot slot = this.getSlotAtPosition(p_146286_1_, p_146286_2_); int l = this.guiLeft; int i1 = this.guiTop; diff --git a/src/main/java/net/minecraft/client/network/NetHandlerLoginClient.java b/src/main/java/net/minecraft/client/network/NetHandlerLoginClient.java index c30a8c4..1c3e4ed 100644 --- a/src/main/java/net/minecraft/client/network/NetHandlerLoginClient.java +++ b/src/main/java/net/minecraft/client/network/NetHandlerLoginClient.java @@ -6,6 +6,7 @@ import com.mojang.authlib.minecraft.MinecraftSessionService; import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import cpw.mods.fml.client.FMLClientHandler; import cpw.mods.fml.common.network.internal.FMLNetworkHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @@ -107,7 +108,10 @@ if (p_147232_2_ == EnumConnectionState.PLAY) { - this.field_147393_d.setNetHandler(new NetHandlerPlayClient(this.field_147394_b, this.field_147395_c, this.field_147393_d)); + NetHandlerPlayClient nhpc = new NetHandlerPlayClient(this.field_147394_b, this.field_147395_c, this.field_147393_d); + this.field_147393_d.setNetHandler(nhpc); + FMLClientHandler.instance().setPlayClient(nhpc); + } } diff --git a/src/main/java/net/minecraft/client/network/NetHandlerPlayClient.java b/src/main/java/net/minecraft/client/network/NetHandlerPlayClient.java index 36036ee..8b9a7f2 100644 --- a/src/main/java/net/minecraft/client/network/NetHandlerPlayClient.java +++ b/src/main/java/net/minecraft/client/network/NetHandlerPlayClient.java @@ -1,7 +1,6 @@ package net.minecraft.client.network; import com.google.common.base.Charsets; -import cpw.mods.fml.client.FMLClientHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import io.netty.buffer.ByteBuf; @@ -218,7 +217,6 @@ this.gameController = p_i45061_1_; this.guiScreenServer = p_i45061_2_; this.netManager = p_i45061_3_; - FMLClientHandler.instance().setPlayClient(this); } public void cleanup() diff --git a/src/main/java/net/minecraft/client/renderer/EntityRenderer.java b/src/main/java/net/minecraft/client/renderer/EntityRenderer.java index b6530ca..f997715 100644 --- a/src/main/java/net/minecraft/client/renderer/EntityRenderer.java +++ b/src/main/java/net/minecraft/client/renderer/EntityRenderer.java @@ -1810,6 +1810,13 @@ this.fogColorBlue = f7; } + net.minecraftforge.client.event.EntityViewRenderEvent.FogColors event = new net.minecraftforge.client.event.EntityViewRenderEvent.FogColors(this, entitylivingbase, block, par1, this.fogColorRed, this.fogColorGreen, this.fogColorBlue); + MinecraftForge.EVENT_BUS.post(event); + + this.fogColorRed = event.red; + this.fogColorBlue = event.blue; + this.fogColorGreen = event.green; + GL11.glClearColor(this.fogColorRed, this.fogColorGreen, this.fogColorBlue, 0.0F); } @@ -1845,6 +1852,13 @@ Block block = ActiveRenderInfo.getBlockAtEntityViewpoint(this.mc.theWorld, entitylivingbase, par2); float f1; + net.minecraftforge.client.event.EntityViewRenderEvent.FogDensity event = new net.minecraftforge.client.event.EntityViewRenderEvent.FogDensity(this, entitylivingbase, block, 0.1F, par2); + + if (MinecraftForge.EVENT_BUS.post(event)) + { + GL11.glFogf(GL11.GL_FOG_DENSITY, event.density); + } + else if (entitylivingbase.isPotionActive(Potion.blindness)) { f1 = 5.0F; diff --git a/src/main/java/net/minecraft/client/renderer/RenderBlocks.java b/src/main/java/net/minecraft/client/renderer/RenderBlocks.java index bbb8dc0..62f89df 100644 --- a/src/main/java/net/minecraft/client/renderer/RenderBlocks.java +++ b/src/main/java/net/minecraft/client/renderer/RenderBlocks.java @@ -8222,4 +8222,18 @@ return (IIcon)p_147758_1_; } + + /*==================================== FORGE START ===========================================*/ + private static RenderBlocks instance; + /** + * Returns a single lazy loaded instance of RenderBlocks, for use in mods who + * don't care about the interaction of other objects on the current state of the RenderBlocks they are using. + * @return A global instance of RenderBlocks + */ + public static RenderBlocks getInstance() + { + if (instance == null) instance = new RenderBlocks(); + return instance; + } + /*==================================== FORGE END =============================================*/ } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/renderer/ThreadDownloadImageData.java b/src/main/java/net/minecraft/client/renderer/ThreadDownloadImageData.java index 59cf553..6fd0e50 100644 --- a/src/main/java/net/minecraft/client/renderer/ThreadDownloadImageData.java +++ b/src/main/java/net/minecraft/client/renderer/ThreadDownloadImageData.java @@ -86,23 +86,23 @@ httpurlconnection.setDoOutput(false); httpurlconnection.connect(); - if (httpurlconnection.getResponseCode() / 100 == 2) + if (httpurlconnection.getResponseCode() / 100 != 2) { - BufferedImage bufferedimage = ImageIO.read(httpurlconnection.getInputStream()); - - if (ThreadDownloadImageData.this.imageBuffer != null) - { - bufferedimage = ThreadDownloadImageData.this.imageBuffer.parseUserSkin(bufferedimage); - } - - ThreadDownloadImageData.this.setBufferedImage(bufferedimage); return; } + + BufferedImage bufferedimage = ImageIO.read(httpurlconnection.getInputStream()); + + if (ThreadDownloadImageData.this.imageBuffer != null) + { + bufferedimage = ThreadDownloadImageData.this.imageBuffer.parseUserSkin(bufferedimage); + } + + ThreadDownloadImageData.this.setBufferedImage(bufferedimage); } catch (Exception exception) { ThreadDownloadImageData.logger.error("Couldn\'t download http texture", exception); - return; } finally { diff --git a/src/main/java/net/minecraft/client/renderer/WorldRenderer.java b/src/main/java/net/minecraft/client/renderer/WorldRenderer.java index d1024db..bb24e96 100644 --- a/src/main/java/net/minecraft/client/renderer/WorldRenderer.java +++ b/src/main/java/net/minecraft/client/renderer/WorldRenderer.java @@ -132,6 +132,7 @@ { ++chunksUpdated; RenderBlocks renderblocks = new RenderBlocks(chunkcache); + net.minecraftforge.client.ForgeHooksClient.setWorldRendererRB(renderblocks); this.bytesDrawn = 0; this.vertexState = null; @@ -212,6 +213,7 @@ break; } } + net.minecraftforge.client.ForgeHooksClient.setWorldRendererRB(null); } HashSet hashset1 = new HashSet(); @@ -234,6 +236,7 @@ GL11.glTranslatef(-8.0F, -8.0F, -8.0F); GL11.glScalef(f, f, f); GL11.glTranslatef(8.0F, 8.0F, 8.0F); + net.minecraftforge.client.ForgeHooksClient.onPreRenderWorld(this, p_147890_1_); Tessellator.instance.startDrawingQuads(); Tessellator.instance.setTranslation((double)(-this.posX), (double)(-this.posY), (double)(-this.posZ)); } @@ -245,8 +248,8 @@ this.vertexState = Tessellator.instance.getVertexState((float)p_147891_2_.posX, (float)p_147891_2_.posY, (float)p_147891_2_.posZ); } - //ForgeHooksClient.afterRenderPass(l1); Noop fo now, TODO: Event if anyone needs this.bytesDrawn += Tessellator.instance.draw(); + net.minecraftforge.client.ForgeHooksClient.onPostRenderWorld(this, p_147891_1_); GL11.glPopMatrix(); GL11.glEndList(); Tessellator.instance.setTranslation(0.0D, 0.0D, 0.0D); diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderItem.java b/src/main/java/net/minecraft/client/renderer/entity/RenderItem.java index baa8cb7..3e096c2 100644 --- a/src/main/java/net/minecraft/client/renderer/entity/RenderItem.java +++ b/src/main/java/net/minecraft/client/renderer/entity/RenderItem.java @@ -755,5 +755,17 @@ { return original; } + + private static RenderItem instance; + /** + * Returns a single lazy loaded instance of RenderItem, for use in mods who + * don't care about the interaction of other objects on the current state of the RenderItem they are using. + * @return A global instance of RenderItem + */ + public static RenderItem getInstance() + { + if (instance == null) instance = new RenderItem(); + return instance; + } /*==================================== FORGE END =============================================*/ } \ No newline at end of file diff --git a/src/main/java/net/minecraft/entity/EntityLivingBase.java b/src/main/java/net/minecraft/entity/EntityLivingBase.java index 5166f50..bb3e3a5 100644 --- a/src/main/java/net/minecraft/entity/EntityLivingBase.java +++ b/src/main/java/net/minecraft/entity/EntityLivingBase.java @@ -799,9 +799,9 @@ this.recentlyHit = 100; this.attackingPlayer = (EntityPlayer)entity; } - else if (entity instanceof EntityWolf) + else if (entity instanceof net.minecraft.entity.passive.EntityTameable) { - EntityWolf entitywolf = (EntityWolf)entity; + net.minecraft.entity.passive.EntityTameable entitywolf = (net.minecraft.entity.passive.EntityTameable)entity; if (entitywolf.isTamed()) { diff --git a/src/main/java/net/minecraft/entity/EntityTracker.java b/src/main/java/net/minecraft/entity/EntityTracker.java index 06273f8..6ffb447 100644 --- a/src/main/java/net/minecraft/entity/EntityTracker.java +++ b/src/main/java/net/minecraft/entity/EntityTracker.java @@ -337,4 +337,23 @@ } } } + + /* ======================================== FORGE START =====================================*/ + + // don't expose the EntityTrackerEntry directly so mods can't mess with the data in there as easily + /** + * Get all players tracking the given Entity. The Entity must be part of the World that this Tracker belongs to. + * @param entity the Entity + * @return all players tracking the Entity + */ + public Set getTrackingPlayers(Entity entity) + { + EntityTrackerEntry entry = (EntityTrackerEntry) trackedEntityIDs.lookup(entity.getEntityId()); + if (entry == null) + return java.util.Collections.emptySet(); + else + return java.util.Collections.unmodifiableSet(entry.trackingPlayers); + } + + /* ======================================== FORGE END =====================================*/ } \ No newline at end of file diff --git a/src/main/java/net/minecraft/entity/EntityTrackerEntry.java b/src/main/java/net/minecraft/entity/EntityTrackerEntry.java index f3fb2c0..83c5748 100644 --- a/src/main/java/net/minecraft/entity/EntityTrackerEntry.java +++ b/src/main/java/net/minecraft/entity/EntityTrackerEntry.java @@ -436,12 +436,14 @@ par1EntityPlayerMP.playerNetServerHandler.sendPacket(new S1DPacketEntityEffect(this.myEntity.getEntityId(), potioneffect)); } } + net.minecraftforge.event.ForgeEventFactory.onStartEntityTracking(myEntity, par1EntityPlayerMP); } } else if (this.trackingPlayers.contains(par1EntityPlayerMP)) { this.trackingPlayers.remove(par1EntityPlayerMP); par1EntityPlayerMP.destroyedItemsNetCache.add(Integer.valueOf(this.myEntity.getEntityId())); + net.minecraftforge.event.ForgeEventFactory.onStopEntityTracking(myEntity, par1EntityPlayerMP); } } } diff --git a/src/main/java/net/minecraft/entity/item/EntityEnderPearl.java b/src/main/java/net/minecraft/entity/item/EntityEnderPearl.java index 141858a..728b9ce 100644 --- a/src/main/java/net/minecraft/entity/item/EntityEnderPearl.java +++ b/src/main/java/net/minecraft/entity/item/EntityEnderPearl.java @@ -60,9 +60,9 @@ this.getThrower().mountEntity((Entity)null); } - this.getThrower().setPositionAndUpdate(this.posX, this.posY, this.posZ); + this.getThrower().setPositionAndUpdate(event.targetX, event.targetY, event.targetZ); this.getThrower().fallDistance = 0.0F; - this.getThrower().attackEntityFrom(DamageSource.fall, 5.0F); + this.getThrower().attackEntityFrom(DamageSource.fall, event.attackDamage); } } } diff --git a/src/main/java/net/minecraft/entity/monster/EntityMagmaCube.java b/src/main/java/net/minecraft/entity/monster/EntityMagmaCube.java index e2f0ea4..b0020e1 100644 --- a/src/main/java/net/minecraft/entity/monster/EntityMagmaCube.java +++ b/src/main/java/net/minecraft/entity/monster/EntityMagmaCube.java @@ -99,6 +99,7 @@ { this.motionY = (double)(0.42F + (float)this.getSlimeSize() * 0.1F); this.isAirBorne = true; + net.minecraftforge.common.ForgeHooks.onLivingJump(this); } protected void fall(float par1) {} diff --git a/src/main/java/net/minecraft/entity/passive/EntityHorse.java b/src/main/java/net/minecraft/entity/passive/EntityHorse.java index 6f80e71..64ee8a7 100644 --- a/src/main/java/net/minecraft/entity/passive/EntityHorse.java +++ b/src/main/java/net/minecraft/entity/passive/EntityHorse.java @@ -1227,6 +1227,7 @@ } this.jumpPower = 0.0F; + net.minecraftforge.common.ForgeHooks.onLivingJump(this); } this.stepHeight = 1.0F; diff --git a/src/main/java/net/minecraft/entity/player/EntityPlayer.java b/src/main/java/net/minecraft/entity/player/EntityPlayer.java index 7603de7..ffdebd3 100644 --- a/src/main/java/net/minecraft/entity/player/EntityPlayer.java +++ b/src/main/java/net/minecraft/entity/player/EntityPlayer.java @@ -2035,6 +2035,7 @@ { getEntityData().setTag(PERSISTED_NBT_TAG, old.getCompoundTag(PERSISTED_NBT_TAG)); } + MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.player.PlayerEvent.Clone(this, par1EntityPlayer, !par2)); } protected boolean canTriggerWalking() diff --git a/src/main/java/net/minecraft/network/NetHandlerPlayServer.java b/src/main/java/net/minecraft/network/NetHandlerPlayServer.java index 429851a..d26cc06 100644 --- a/src/main/java/net/minecraft/network/NetHandlerPlayServer.java +++ b/src/main/java/net/minecraft/network/NetHandlerPlayServer.java @@ -529,7 +529,7 @@ return; } - PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(playerEntity, PlayerInteractEvent.Action.RIGHT_CLICK_AIR, 0, 0, 0, -1); + PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(playerEntity, PlayerInteractEvent.Action.RIGHT_CLICK_AIR, 0, 0, 0, -1, worldserver); if (event.useItem != Event.Result.DENY) { this.playerEntity.theItemInWorldManager.tryUseItem(this.playerEntity, worldserver, itemstack); diff --git a/src/main/java/net/minecraft/network/PingResponseHandler.java b/src/main/java/net/minecraft/network/PingResponseHandler.java index ff1ab04..de5220a 100644 --- a/src/main/java/net/minecraft/network/PingResponseHandler.java +++ b/src/main/java/net/minecraft/network/PingResponseHandler.java @@ -30,8 +30,13 @@ try { - if (bytebuf.readUnsignedByte() == 254) + try { + if (bytebuf.readUnsignedByte() != 254) + { + return; + } + InetSocketAddress inetsocketaddress = (InetSocketAddress)p_channelRead_1_.channel().remoteAddress(); MinecraftServer minecraftserver = this.field_151257_b.func_151267_d(); int i = bytebuf.readableBytes(); @@ -76,12 +81,11 @@ bytebuf.release(); flag = false; - return; } - } - catch (RuntimeException runtimeexception) - { - return; + catch (RuntimeException runtimeexception) + { + ; + } } finally { diff --git a/src/main/java/net/minecraft/network/rcon/RConThreadClient.java b/src/main/java/net/minecraft/network/rcon/RConThreadClient.java index 74cc342..ba0c809 100644 --- a/src/main/java/net/minecraft/network/rcon/RConThreadClient.java +++ b/src/main/java/net/minecraft/network/rcon/RConThreadClient.java @@ -53,16 +53,16 @@ BufferedInputStream bufferedinputstream = new BufferedInputStream(this.clientSocket.getInputStream()); int i = bufferedinputstream.read(this.buffer, 0, 1460); - if (10 > i) + if (10 <= i) { - return; - } + byte b0 = 0; + int j = RConUtils.getBytesAsLEInt(this.buffer, 0, i); - byte b0 = 0; - int j = RConUtils.getBytesAsLEInt(this.buffer, 0, i); + if (j != i - 4) + { + return; + } - if (j == i - 4) - { int i1 = b0 + 4; int k = RConUtils.getBytesAsLEInt(this.buffer, i1, i); i1 += 4; diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 5308180..f3cf2f2 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -577,6 +577,7 @@ public void updateTimeLightAndEntities() { this.theProfiler.startSection("levels"); + net.minecraftforge.common.chunkio.ChunkIOExecutor.tick(); int i; Integer[] ids = DimensionManager.getIDs(this.tickCounter % 200 == 0); diff --git a/src/main/java/net/minecraft/server/management/ItemInWorldManager.java b/src/main/java/net/minecraft/server/management/ItemInWorldManager.java index 9aba6bb..bf702e3 100644 --- a/src/main/java/net/minecraft/server/management/ItemInWorldManager.java +++ b/src/main/java/net/minecraft/server/management/ItemInWorldManager.java @@ -137,7 +137,7 @@ { if (!this.gameType.isAdventure() || this.thisPlayerMP.isCurrentToolAdventureModeExempt(par1, par2, par3)) { - PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(thisPlayerMP, Action.LEFT_CLICK_BLOCK, par1, par2, par3, par4); + PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(thisPlayerMP, Action.LEFT_CLICK_BLOCK, par1, par2, par3, par4, theWorld); if (event.isCanceled()) { thisPlayerMP.playerNetServerHandler.sendPacket(new S23PacketBlockChange(par1, par2, par3, theWorld)); @@ -346,7 +346,7 @@ public boolean activateBlockOrUseItem(EntityPlayer par1EntityPlayer, World par2World, ItemStack par3ItemStack, int par4, int par5, int par6, int par7, float par8, float par9, float par10) { - PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(par1EntityPlayer, Action.RIGHT_CLICK_BLOCK, par4, par5, par6, par7); + PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(par1EntityPlayer, Action.RIGHT_CLICK_BLOCK, par4, par5, par6, par7, par2World); if (event.isCanceled()) { thisPlayerMP.playerNetServerHandler.sendPacket(new S23PacketBlockChange(par4, par5, par6, theWorld)); diff --git a/src/main/java/net/minecraft/server/management/PlayerManager.java b/src/main/java/net/minecraft/server/management/PlayerManager.java index 7dbc76b..d4f289d 100644 --- a/src/main/java/net/minecraft/server/management/PlayerManager.java +++ b/src/main/java/net/minecraft/server/management/PlayerManager.java @@ -2,7 +2,10 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.List; + import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.Packet; import net.minecraft.network.play.server.S21PacketChunkData; @@ -16,6 +19,8 @@ import net.minecraft.world.chunk.Chunk; import net.minecraftforge.common.ForgeModContainer; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.chunkio.ChunkIOExecutor; +import net.minecraftforge.common.util.ChunkCoordComparator; import net.minecraftforge.event.world.ChunkWatchEvent; public class PlayerManager @@ -124,15 +129,24 @@ int j = (int)par1EntityPlayerMP.posZ >> 4; par1EntityPlayerMP.managedPosX = par1EntityPlayerMP.posX; par1EntityPlayerMP.managedPosZ = par1EntityPlayerMP.posZ; + // Load nearby chunks first + List chunkList = new ArrayList(); for (int k = i - this.playerViewRadius; k <= i + this.playerViewRadius; ++k) { for (int l = j - this.playerViewRadius; l <= j + this.playerViewRadius; ++l) { - this.getOrCreateChunkWatcher(k, l, true).addPlayer(par1EntityPlayerMP); + chunkList.add(new ChunkCoordIntPair(k, l)); } } + Collections.sort(chunkList, new ChunkCoordComparator(par1EntityPlayerMP)); + + for (ChunkCoordIntPair pair : chunkList) + { + this.getOrCreateChunkWatcher(pair.chunkXPos, pair.chunkZPos, true).addPlayer(par1EntityPlayerMP); + } + this.players.add(par1EntityPlayerMP); this.filterChunkLoadQueue(par1EntityPlayerMP); } @@ -234,6 +248,7 @@ int i1 = this.playerViewRadius; int j1 = i - k; int k1 = j - l; + List chunksToLoad = new ArrayList(); if (j1 != 0 || k1 != 0) { @@ -243,7 +258,7 @@ { if (!this.overlaps(l1, i2, k, l, i1)) { - this.getOrCreateChunkWatcher(l1, i2, true).addPlayer(par1EntityPlayerMP); + chunksToLoad.add(new ChunkCoordIntPair(l1, i2)); } if (!this.overlaps(l1 - j1, i2 - k1, i, j, i1)) @@ -261,6 +276,18 @@ this.filterChunkLoadQueue(par1EntityPlayerMP); par1EntityPlayerMP.managedPosX = par1EntityPlayerMP.posX; par1EntityPlayerMP.managedPosZ = par1EntityPlayerMP.posZ; + // send nearest chunks first + Collections.sort(chunksToLoad, new ChunkCoordComparator(par1EntityPlayerMP)); + + for (ChunkCoordIntPair pair : chunksToLoad) + { + this.getOrCreateChunkWatcher(pair.chunkXPos, pair.chunkZPos, true).addPlayer(par1EntityPlayerMP); + } + + if (i1 > 1 || i1 < -1 || j1 > 1 || j1 < -1) + { + Collections.sort(par1EntityPlayerMP.loadedChunks, new ChunkCoordComparator(par1EntityPlayerMP)); + } } } } @@ -284,15 +311,24 @@ private int numberOfTilesToUpdate; private int flagsYAreasToUpdate; private long previousWorldTime; + private final HashMap players = new HashMap(); + private boolean loaded = false; + private Runnable loadedRunnable = new Runnable() + { + public void run() + { + PlayerInstance.this.loaded = true; + } + }; private static final String __OBFID = "CL_00001435"; public PlayerInstance(int par2, int par3) { this.chunkLocation = new ChunkCoordIntPair(par2, par3); - PlayerManager.this.getWorldServer().theChunkProviderServer.loadChunk(par2, par3); + PlayerManager.this.theWorldServer.theChunkProviderServer.loadChunk(par2, par3, this.loadedRunnable); } - public void addPlayer(EntityPlayerMP par1EntityPlayerMP) + public void addPlayer(final EntityPlayerMP par1EntityPlayerMP) { if (this.playersWatchingChunk.contains(par1EntityPlayerMP)) { @@ -306,7 +342,26 @@ } this.playersWatchingChunk.add(par1EntityPlayerMP); - par1EntityPlayerMP.loadedChunks.add(this.chunkLocation); + Runnable playerRunnable; + + if (this.loaded) + { + playerRunnable = null; + par1EntityPlayerMP.loadedChunks.add(this.chunkLocation); + } + else + { + playerRunnable = new Runnable() + { + public void run() + { + par1EntityPlayerMP.loadedChunks.add(PlayerInstance.this.chunkLocation); + } + }; + PlayerManager.this.getWorldServer().theChunkProviderServer.loadChunk(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos, playerRunnable); + } + + this.players.put(par1EntityPlayerMP, playerRunnable); } } @@ -314,6 +369,24 @@ { if (this.playersWatchingChunk.contains(par1EntityPlayerMP)) { + // If we haven't loaded yet don't load the chunk just so we can clean it up + if (!this.loaded) + { + ChunkIOExecutor.dropQueuedChunkLoad(PlayerManager.this.getWorldServer(), this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos, this.players.get(par1EntityPlayerMP)); + this.playersWatchingChunk.remove(par1EntityPlayerMP); + this.players.remove(par1EntityPlayerMP); + + if (this.playersWatchingChunk.isEmpty()) + { + ChunkIOExecutor.dropQueuedChunkLoad(PlayerManager.this.getWorldServer(), this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos, this.loadedRunnable); + long i = (long) this.chunkLocation.chunkXPos + 2147483647L | (long) this.chunkLocation.chunkZPos + 2147483647L << 32; + PlayerManager.this.playerInstances.remove(i); + PlayerManager.this.playerInstanceList.remove(this); + } + + return; + } + Chunk chunk = PlayerManager.this.theWorldServer.getChunkFromChunkCoords(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos); if (chunk.func_150802_k()) @@ -321,6 +394,7 @@ par1EntityPlayerMP.playerNetServerHandler.sendPacket(new S21PacketChunkData(chunk, true, 0)); } + this.players.remove(par1EntityPlayerMP); this.playersWatchingChunk.remove(par1EntityPlayerMP); par1EntityPlayerMP.loadedChunks.remove(this.chunkLocation); diff --git a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java index 7921923..d8003f5 100644 --- a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java +++ b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java @@ -7,6 +7,7 @@ import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; + import java.io.File; import java.net.SocketAddress; import java.text.SimpleDateFormat; @@ -18,6 +19,7 @@ import java.util.Map; import java.util.Set; import java.util.Map.Entry; + import net.minecraft.entity.Entity; import net.minecraft.entity.EntityList; import net.minecraft.entity.player.EntityPlayer; @@ -61,6 +63,8 @@ import net.minecraft.world.WorldSettings; import net.minecraft.world.demo.DemoWorldManager; import net.minecraft.world.storage.IPlayerFileData; +import net.minecraftforge.common.chunkio.ChunkIOExecutor; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -246,6 +250,7 @@ this.sendPacketToAllPlayers(new S38PacketPlayerListItem(par1EntityPlayerMP.getCommandSenderName(), true, 1000)); this.playerEntityList.add(par1EntityPlayerMP); WorldServer worldserver = this.mcServer.worldServerForDimension(par1EntityPlayerMP.dimension); + ChunkIOExecutor.adjustPoolSize(this.getCurrentPlayerCount()); worldserver.spawnEntityInWorld(par1EntityPlayerMP); this.func_72375_a(par1EntityPlayerMP, (WorldServer)null); @@ -278,6 +283,7 @@ worldserver.getPlayerManager().removePlayer(par1EntityPlayerMP); this.playerEntityList.remove(par1EntityPlayerMP); this.field_148547_k.remove(par1EntityPlayerMP.getCommandSenderName()); + ChunkIOExecutor.adjustPoolSize(this.getCurrentPlayerCount()); this.sendPacketToAllPlayers(new S38PacketPlayerListItem(par1EntityPlayerMP.getCommandSenderName(), false, 9999)); } diff --git a/src/main/java/net/minecraft/tileentity/TileEntityNote.java b/src/main/java/net/minecraft/tileentity/TileEntityNote.java index 2ac498d..0970caa 100644 --- a/src/main/java/net/minecraft/tileentity/TileEntityNote.java +++ b/src/main/java/net/minecraft/tileentity/TileEntityNote.java @@ -35,7 +35,9 @@ public void changePitch() { + byte old = note; this.note = (byte)((this.note + 1) % 25); + if (!net.minecraftforge.common.ForgeHooks.onNoteChange(this, old)) return; this.markDirty(); } diff --git a/src/main/java/net/minecraft/util/AxisAlignedBB.java b/src/main/java/net/minecraft/util/AxisAlignedBB.java index 740650b..8545b47 100644 --- a/src/main/java/net/minecraft/util/AxisAlignedBB.java +++ b/src/main/java/net/minecraft/util/AxisAlignedBB.java @@ -23,6 +23,7 @@ return new AxisAlignedBB(par0, par2, par4, par6, par8, par10); } + @Deprecated /* This method has been deprecated as it no longer exists in 1.7.10. Use getBoundingBox directly. */ public static AABBPool getAABBPool() { return (AABBPool)theAABBLocalPool.get(); diff --git a/src/main/java/net/minecraft/util/HttpUtil.java b/src/main/java/net/minecraft/util/HttpUtil.java index dfd1aa5..0b02dfe 100644 --- a/src/main/java/net/minecraft/util/HttpUtil.java +++ b/src/main/java/net/minecraft/util/HttpUtil.java @@ -143,109 +143,105 @@ try { - try + byte[] abyte = new byte[4096]; + URL url = new URL(p_151223_1_); + urlconnection = url.openConnection(p_151223_6_); + float f = 0.0F; + float f1 = (float)p_151223_3_.entrySet().size(); + Iterator iterator = p_151223_3_.entrySet().iterator(); + + while (iterator.hasNext()) { - byte[] abyte = new byte[4096]; - URL url = new URL(p_151223_1_); - urlconnection = url.openConnection(p_151223_6_); - float f = 0.0F; - float f1 = (float)p_151223_3_.entrySet().size(); - Iterator iterator = p_151223_3_.entrySet().iterator(); - - while (iterator.hasNext()) - { - Entry entry = (Entry)iterator.next(); - urlconnection.setRequestProperty((String)entry.getKey(), (String)entry.getValue()); - - if (p_151223_5_ != null) - { - p_151223_5_.setLoadingProgress((int)(++f / f1 * 100.0F)); - } - } - - inputstream = urlconnection.getInputStream(); - f1 = (float)urlconnection.getContentLength(); - int i = urlconnection.getContentLength(); + Entry entry = (Entry)iterator.next(); + urlconnection.setRequestProperty((String)entry.getKey(), (String)entry.getValue()); if (p_151223_5_ != null) { - p_151223_5_.resetProgresAndWorkingMessage(String.format("Downloading file (%.2f MB)...", new Object[] {Float.valueOf(f1 / 1000.0F / 1000.0F)})); + p_151223_5_.setLoadingProgress((int)(++f / f1 * 100.0F)); } + } - if (p_151223_0_.exists()) + inputstream = urlconnection.getInputStream(); + f1 = (float)urlconnection.getContentLength(); + int i = urlconnection.getContentLength(); + + if (p_151223_5_ != null) + { + p_151223_5_.resetProgresAndWorkingMessage(String.format("Downloading file (%.2f MB)...", new Object[] {Float.valueOf(f1 / 1000.0F / 1000.0F)})); + } + + if (p_151223_0_.exists()) + { + long j = p_151223_0_.length(); + + if (j == (long)i) { - long j = p_151223_0_.length(); + p_151223_2_.func_148522_a(p_151223_0_); - if (j == (long)i) + if (p_151223_5_ != null) { - p_151223_2_.func_148522_a(p_151223_0_); - - if (p_151223_5_ != null) - { - p_151223_5_.func_146586_a(); - } - - return; + p_151223_5_.func_146586_a(); } - HttpUtil.logger.warn("Deleting " + p_151223_0_ + " as it does not match what we currently have (" + i + " vs our " + j + ")."); - p_151223_0_.delete(); + return; } - else if (p_151223_0_.getParentFile() != null) + + HttpUtil.logger.warn("Deleting " + p_151223_0_ + " as it does not match what we currently have (" + i + " vs our " + j + ")."); + p_151223_0_.delete(); + } + else if (p_151223_0_.getParentFile() != null) + { + p_151223_0_.getParentFile().mkdirs(); + } + + dataoutputstream = new DataOutputStream(new FileOutputStream(p_151223_0_)); + + if (p_151223_4_ > 0 && f1 > (float)p_151223_4_) + { + if (p_151223_5_ != null) { - p_151223_0_.getParentFile().mkdirs(); + p_151223_5_.func_146586_a(); } - dataoutputstream = new DataOutputStream(new FileOutputStream(p_151223_0_)); + throw new IOException("Filesize is bigger than maximum allowed (file is " + f + ", limit is " + p_151223_4_ + ")"); + } - if (p_151223_4_ > 0 && f1 > (float)p_151223_4_) + boolean flag = false; + int k; + + while ((k = inputstream.read(abyte)) >= 0) + { + f += (float)k; + + if (p_151223_5_ != null) + { + p_151223_5_.setLoadingProgress((int)(f / f1 * 100.0F)); + } + + if (p_151223_4_ > 0 && f > (float)p_151223_4_) { if (p_151223_5_ != null) { p_151223_5_.func_146586_a(); } - throw new IOException("Filesize is bigger than maximum allowed (file is " + f + ", limit is " + p_151223_4_ + ")"); + throw new IOException("Filesize was bigger than maximum allowed (got >= " + f + ", limit was " + p_151223_4_ + ")"); } - boolean flag = false; - int k; - - while ((k = inputstream.read(abyte)) >= 0) - { - f += (float)k; - - if (p_151223_5_ != null) - { - p_151223_5_.setLoadingProgress((int)(f / f1 * 100.0F)); - } - - if (p_151223_4_ > 0 && f > (float)p_151223_4_) - { - if (p_151223_5_ != null) - { - p_151223_5_.func_146586_a(); - } - - throw new IOException("Filesize was bigger than maximum allowed (got >= " + f + ", limit was " + p_151223_4_ + ")"); - } - - dataoutputstream.write(abyte, 0, k); - } - - p_151223_2_.func_148522_a(p_151223_0_); - - if (p_151223_5_ != null) - { - p_151223_5_.func_146586_a(); - return; - } + dataoutputstream.write(abyte, 0, k); } - catch (Throwable throwable) + + p_151223_2_.func_148522_a(p_151223_0_); + + if (p_151223_5_ != null) { - throwable.printStackTrace(); + p_151223_5_.func_146586_a(); } } + catch (Throwable throwable) + { + throwable.printStackTrace(); + } finally { try diff --git a/src/main/java/net/minecraft/util/ObjectIntIdentityMap.java b/src/main/java/net/minecraft/util/ObjectIntIdentityMap.java index 83c6aab..e70aca3 100644 --- a/src/main/java/net/minecraft/util/ObjectIntIdentityMap.java +++ b/src/main/java/net/minecraft/util/ObjectIntIdentityMap.java @@ -2,20 +2,21 @@ import com.google.common.base.Predicates; import com.google.common.collect.Iterators; -import gnu.trove.map.hash.TIntIntHashMap; import java.util.ArrayList; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; public class ObjectIntIdentityMap implements IObjectIntIterable { - protected TIntIntHashMap field_148749_a = new TIntIntHashMap(256, 0.5F, -1, -1); + protected Map field_148749_a = new IdentityHashMap(512); protected List field_148748_b = new ArrayList(); private static final String __OBFID = "CL_00001203"; public void func_148746_a(Object p_148746_1_, int p_148746_2_) { - this.field_148749_a.put(System.identityHashCode(p_148746_1_), p_148746_2_); + this.field_148749_a.put(p_148746_1_, p_148746_2_); while (this.field_148748_b.size() <= p_148746_2_) { @@ -27,7 +28,8 @@ public int func_148747_b(Object p_148747_1_) { - return this.field_148749_a.get(System.identityHashCode(p_148747_1_)); + Integer ret = this.field_148749_a.get(p_148747_1_); + return ret != null ? ret : -1; } public Object func_148745_a(int p_148745_1_) diff --git a/src/main/java/net/minecraft/world/biome/BiomeDecorator.java b/src/main/java/net/minecraft/world/biome/BiomeDecorator.java index 43c3fef..d942467 100644 --- a/src/main/java/net/minecraft/world/biome/BiomeDecorator.java +++ b/src/main/java/net/minecraft/world/biome/BiomeDecorator.java @@ -178,7 +178,7 @@ { k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - i1 = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(k, l) + 32); + i1 = nextInt(this.currentWorld.getHeightValue(k, l) + 32); String s = p_150513_1_.func_150572_a(this.randomGenerator, k, i1, l); BlockFlower blockflower = BlockFlower.func_149857_e(s); @@ -194,7 +194,7 @@ { k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - i1 = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(k, l) * 2); + i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); WorldGenerator worldgenerator = p_150513_1_.getRandomWorldGenForGrass(this.randomGenerator); worldgenerator.generate(this.currentWorld, this.randomGenerator, k, i1, l); } @@ -204,7 +204,7 @@ { k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - i1 = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(k, l) * 2); + i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); (new WorldGenDeadBush(Blocks.deadbush)).generate(this.currentWorld, this.randomGenerator, k, i1, l); } @@ -214,7 +214,7 @@ k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - for (i1 = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(k, l) * 2); i1 > 0 && this.currentWorld.isAirBlock(k, i1 - 1, l); --i1) + for (i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); i1 > 0 && this.currentWorld.isAirBlock(k, i1 - 1, l); --i1) { ; } @@ -237,7 +237,7 @@ { k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - i1 = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(k, l) * 2); + i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); this.mushroomRedGen.generate(this.currentWorld, this.randomGenerator, k, i1, l); } } @@ -246,7 +246,7 @@ { j = this.chunk_X + this.randomGenerator.nextInt(16) + 8; k = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - l = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(j, k) * 2); + l = nextInt(this.currentWorld.getHeightValue(j, k) * 2); this.mushroomBrownGen.generate(this.currentWorld, this.randomGenerator, j, l, k); } @@ -254,7 +254,7 @@ { j = this.chunk_X + this.randomGenerator.nextInt(16) + 8; k = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - l = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(j, k) * 2); + l = nextInt(this.currentWorld.getHeightValue(j, k) * 2); this.mushroomRedGen.generate(this.currentWorld, this.randomGenerator, j, l, k); } @@ -263,7 +263,7 @@ { k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - i1 = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(k, l) * 2); + i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); this.reedGen.generate(this.currentWorld, this.randomGenerator, k, i1, l); } @@ -271,7 +271,7 @@ { k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - i1 = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(k, l) * 2); + i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); this.reedGen.generate(this.currentWorld, this.randomGenerator, k, i1, l); } @@ -280,7 +280,7 @@ { j = this.chunk_X + this.randomGenerator.nextInt(16) + 8; k = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - l = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(j, k) * 2); + l = nextInt(this.currentWorld.getHeightValue(j, k) * 2); (new WorldGenPumpkin()).generate(this.currentWorld, this.randomGenerator, j, l, k); } @@ -289,7 +289,7 @@ { k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; - i1 = this.randomGenerator.nextInt(this.currentWorld.getHeightValue(k, l) * 2); + i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); this.cactusGen.generate(this.currentWorld, this.randomGenerator, k, i1, l); } @@ -359,4 +359,10 @@ this.genStandardOre2(1, this.lapisGen, 16, 16); MinecraftForge.ORE_GEN_BUS.post(new OreGenEvent.Post(currentWorld, randomGenerator, chunk_X, chunk_Z)); } + + private int nextInt(int i) { + if (i <= 1) + return 0; + return this.randomGenerator.nextInt(i); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenForest.java b/src/main/java/net/minecraft/world/biome/BiomeGenForest.java index 13b72aa..9433e1c 100644 --- a/src/main/java/net/minecraft/world/biome/BiomeGenForest.java +++ b/src/main/java/net/minecraft/world/biome/BiomeGenForest.java @@ -60,7 +60,7 @@ this.flowers.clear(); for (int x = 0; x < BlockFlower.field_149859_a.length; x++) { - this.addFlower(Blocks.red_flower, x, 10); + this.addFlower(Blocks.red_flower, x == 1 ? 0 : x, 10); } } } diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenJungle.java b/src/main/java/net/minecraft/world/biome/BiomeGenJungle.java index 6f045a3..ffdc22e 100644 --- a/src/main/java/net/minecraft/world/biome/BiomeGenJungle.java +++ b/src/main/java/net/minecraft/world/biome/BiomeGenJungle.java @@ -59,7 +59,9 @@ super.decorate(par1World, par2Random, par3, par4); int k = par3 + par2Random.nextInt(16) + 8; int l = par4 + par2Random.nextInt(16) + 8; - int i1 = par2Random.nextInt(par1World.getHeightValue(k, l) * 2); + int height = par1World.getHeightValue(k, l) * 2; //This was the original input for the nextInt below. But it could == 0, which crashes nextInt + if (height < 1) height = 1; + int i1 = par2Random.nextInt(height); (new WorldGenMelon()).generate(par1World, par2Random, k, i1, l); WorldGenVines worldgenvines = new WorldGenVines(); diff --git a/src/main/java/net/minecraft/world/biome/BiomeGenSwamp.java b/src/main/java/net/minecraft/world/biome/BiomeGenSwamp.java index f6ee3c9..4ccd34f 100644 --- a/src/main/java/net/minecraft/world/biome/BiomeGenSwamp.java +++ b/src/main/java/net/minecraft/world/biome/BiomeGenSwamp.java @@ -31,7 +31,7 @@ this.waterColorMultiplier = 14745518; this.spawnableMonsterList.add(new BiomeGenBase.SpawnListEntry(EntitySlime.class, 1, 1, 1)); this.flowers.clear(); - this.addFlower(Blocks.red_mushroom_block, 0, 10); + this.addFlower(Blocks.red_flower, 1, 10); } public WorldGenAbstractTree func_150567_a(Random p_150567_1_) diff --git a/src/main/java/net/minecraft/world/chunk/Chunk.java b/src/main/java/net/minecraft/world/chunk/Chunk.java index 6c31be4..594f18c 100644 --- a/src/main/java/net/minecraft/world/chunk/Chunk.java +++ b/src/main/java/net/minecraft/world/chunk/Chunk.java @@ -499,7 +499,7 @@ { int x = (xPosition << 4) + p_150808_1_; int z = (zPosition << 4) + p_150808_3_; - return this.getBlock(p_150808_1_, p_150808_2_, p_150808_3_).getLightOpacity(worldObj, x, p_150808_2_, p_150808_3_); + return this.getBlock(p_150808_1_, p_150808_2_, p_150808_3_).getLightOpacity(worldObj, x, p_150808_2_, z); } public Block getBlock(final int p_150810_1_, final int p_150810_2_, final int p_150810_3_) @@ -584,6 +584,8 @@ int l1 = this.xPosition * 16 + p_150807_1_; int i2 = this.zPosition * 16 + p_150807_3_; + + int k2 = block1.getLightOpacity(this.worldObj, l1, p_150807_2_, i2); if (!this.worldObj.isRemote) { @@ -618,8 +620,7 @@ } else { - int j2 = p_150807_4_.getLightOpacity(); - int k2 = block1.getLightOpacity(); + int j2 = p_150807_4_.getLightOpacity(this.worldObj, l1, p_150807_2_, i2); if (j2 > 0) { diff --git a/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java b/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java index 313988e..649d7a3 100644 --- a/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java +++ b/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java @@ -48,8 +48,46 @@ this.chunkSaveLocation = par1File; } + public boolean chunkExists(World world, int i, int j) + { + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j); + + synchronized (this.syncLockObject) + { + if (this.pendingAnvilChunksCoordinates.contains(chunkcoordintpair)) + { + Iterator iter = this.chunksToRemove.iterator(); + while (iter.hasNext()) + { + PendingChunk pendingChunk = (PendingChunk)iter.next(); + if (pendingChunk.chunkCoordinate.equals(chunkcoordintpair)) + { + return true; + } + } + } + } + + return RegionFileCache.createOrLoadRegionFile(this.chunkSaveLocation, i, j).chunkExists(i & 31, j & 31); + } + public Chunk loadChunk(World par1World, int par2, int par3) throws IOException { + Object[] data = this.loadChunk__Async(par1World, par2, par3); + + if (data != null) + { + Chunk chunk = (Chunk) data[0]; + NBTTagCompound nbttagcompound = (NBTTagCompound) data[1]; + this.loadEntities(par1World, nbttagcompound.getCompoundTag("Level"), chunk); + return chunk; + } + + return null; + } + + public Object[] loadChunk__Async(World par1World, int par2, int par3) throws IOException + { NBTTagCompound nbttagcompound = null; ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(par2, par3); Object object = this.syncLockObject; @@ -58,11 +96,13 @@ { if (this.pendingAnvilChunksCoordinates.contains(chunkcoordintpair)) { - for (int k = 0; k < this.chunksToRemove.size(); ++k) + Iterator iter = this.chunksToRemove.iterator(); + while (iter.hasNext()) { - if (((AnvilChunkLoader.PendingChunk)this.chunksToRemove.get(k)).chunkCoordinate.equals(chunkcoordintpair)) + PendingChunk pendingChunk = (PendingChunk)iter.next(); + if (pendingChunk.chunkCoordinate.equals(chunkcoordintpair)) { - nbttagcompound = ((AnvilChunkLoader.PendingChunk)this.chunksToRemove.get(k)).nbtTags; + nbttagcompound = pendingChunk.nbtTags; break; } } @@ -81,11 +121,24 @@ nbttagcompound = CompressedStreamTools.read(datainputstream); } - return this.checkedReadChunkFromNBT(par1World, par2, par3, nbttagcompound); + return this.checkedReadChunkFromNBT__Async(par1World, par2, par3, nbttagcompound); } protected Chunk checkedReadChunkFromNBT(World par1World, int par2, int par3, NBTTagCompound par4NBTTagCompound) { + Object[] data = this.checkedReadChunkFromNBT__Async(par1World, par2, par3, par4NBTTagCompound); + + if (data != null) + { + Chunk chunk = (Chunk) data[0]; + return chunk; + } + + return null; + } + + protected Object[] checkedReadChunkFromNBT__Async(World par1World, int par2, int par3, NBTTagCompound par4NBTTagCompound) + { if (!par4NBTTagCompound.hasKey("Level", 10)) { logger.error("Chunk file at " + par2 + "," + par3 + " is missing level data, skipping"); @@ -105,11 +158,30 @@ logger.error("Chunk file at " + par2 + "," + par3 + " is in the wrong location; relocating. (Expected " + par2 + ", " + par3 + ", got " + chunk.xPosition + ", " + chunk.zPosition + ")"); par4NBTTagCompound.setInteger("xPos", par2); par4NBTTagCompound.setInteger("zPos", par3); + // Have to move tile entities since we don't load them at this stage + NBTTagList tileEntities = par4NBTTagCompound.getCompoundTag("Level").getTagList("TileEntities", 10); + + if (tileEntities != null) + { + for (int te = 0; te < tileEntities.tagCount(); te++) + { + NBTTagCompound tileEntity = (NBTTagCompound) tileEntities.getCompoundTagAt(te); + int x = tileEntity.getInteger("x") - chunk.xPosition * 16; + int z = tileEntity.getInteger("z") - chunk.zPosition * 16; + tileEntity.setInteger("x", par2 * 16 + x); + tileEntity.setInteger("z", par3 * 16 + z); + } + } + chunk = this.readChunkFromNBT(par1World, par4NBTTagCompound.getCompoundTag("Level")); } - MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, par4NBTTagCompound)); - return chunk; + Object[] data = new Object[2]; + data[0] = chunk; + data[1] = par4NBTTagCompound; + // event is fired in ChunkIOProvider.callStage2 since it must be fired after TE's load. + // MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, par4NBTTagCompound)); + return data; } } @@ -377,6 +449,12 @@ chunk.setBiomeArray(par2NBTTagCompound.getByteArray("Biomes")); } + // End this method here and split off entity loading to another method + return chunk; + } + + public void loadEntities(World par1World, NBTTagCompound par2NBTTagCompound, Chunk chunk) + { NBTTagList nbttaglist1 = par2NBTTagCompound.getTagList("Entities", 10); if (nbttaglist1 != null) @@ -438,7 +516,7 @@ } } - return chunk; + // return chunk; } static class PendingChunk diff --git a/src/main/java/net/minecraft/world/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/chunk/storage/RegionFile.java index bac9c3e..e04f79c 100644 --- a/src/main/java/net/minecraft/world/chunk/storage/RegionFile.java +++ b/src/main/java/net/minecraft/world/chunk/storage/RegionFile.java @@ -104,6 +104,39 @@ } } + // This is a copy (sort of) of the method below it, make sure they stay in sync + public synchronized boolean chunkExists(int x, int z) + { + if (this.outOfBounds(x, z)) return false; + + try + { + int offset = this.getOffset(x, z); + + if (offset == 0) return false; + + int sectorNumber = offset >> 8; + int numSectors = offset & 255; + + if (sectorNumber + numSectors > this.sectorFree.size()) return false; + + this.dataFile.seek((long)(sectorNumber * 4096)); + int length = this.dataFile.readInt(); + + if (length > 4096 * numSectors || length <= 0) return false; + + byte version = this.dataFile.readByte(); + + if (version == 1 || version == 2) return true; + } + catch (IOException ioexception) + { + return false; + } + + return false; + } + public synchronized DataInputStream getChunkDataInputStream(int par1, int par2) { if (this.outOfBounds(par1, par2)) diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java index 1027427..bef5bf4 100644 --- a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java @@ -23,9 +23,12 @@ import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.EmptyChunk; import net.minecraft.world.chunk.IChunkProvider; +import net.minecraft.world.chunk.storage.AnvilChunkLoader; import net.minecraft.world.chunk.storage.IChunkLoader; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.ForgeChunkManager; +import net.minecraftforge.common.chunkio.ChunkIOExecutor; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -34,12 +37,13 @@ private static final Logger logger = LogManager.getLogger(); private Set chunksToUnload = new HashSet(); private Chunk defaultEmptyChunk; - private IChunkProvider currentChunkProvider; + public IChunkProvider currentChunkProvider; public IChunkLoader currentChunkLoader; public boolean loadChunkOnProvideRequest = true; - private LongHashMap loadedChunkHashMap = new LongHashMap(); - private List loadedChunks = new ArrayList(); - private WorldServer worldObj; + public LongHashMap loadedChunkHashMap = new LongHashMap(); + public List loadedChunks = new ArrayList(); + public WorldServer worldObj; + private Set loadingChunks = com.google.common.collect.Sets.newHashSet(); private static final String __OBFID = "CL_00001436"; public ChunkProviderServer(WorldServer par1WorldServer, IChunkLoader par2IChunkLoader, IChunkProvider par3IChunkProvider) @@ -88,12 +92,61 @@ public Chunk loadChunk(int par1, int par2) { + return loadChunk(par1, par2, null); + } + + public Chunk loadChunk(int par1, int par2, Runnable runnable) + { + long k = ChunkCoordIntPair.chunkXZ2Int(par1, par2); + this.chunksToUnload.remove(Long.valueOf(k)); + Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(k); + AnvilChunkLoader loader = null; + + if (this.currentChunkLoader instanceof AnvilChunkLoader) + { + loader = (AnvilChunkLoader) this.currentChunkLoader; + } + + // We can only use the queue for already generated chunks + if (chunk == null && loader != null && loader.chunkExists(this.worldObj, par1, par2)) + { + if (runnable != null) + { + ChunkIOExecutor.queueChunkLoad(this.worldObj, loader, this, par1, par2, runnable); + return null; + } + else + { + chunk = ChunkIOExecutor.syncChunkLoad(this.worldObj, loader, this, par1, par2); + } + } + else if (chunk == null) + { + chunk = this.originalLoadChunk(par1, par2); + } + + // If we didn't load the chunk async and have a callback run it now + if (runnable != null) + { + runnable.run(); + } + + return chunk; + } + + public Chunk originalLoadChunk(int par1, int par2) + { long k = ChunkCoordIntPair.chunkXZ2Int(par1, par2); this.chunksToUnload.remove(Long.valueOf(k)); Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(k); if (chunk == null) { + boolean added = loadingChunks.add(k); + if (!added) + { + cpw.mods.fml.common.FMLLog.bigWarning("There is an attempt to load a chunk (%d,%d) in dimension %d that is already being loaded. This will cause weird chunk breakages.", par1, par2, worldObj.provider.dimensionId); + } chunk = ForgeChunkManager.fetchDormantChunk(k, this.worldObj); if (chunk == null) { @@ -126,6 +179,7 @@ this.loadedChunkHashMap.add(k, chunk); this.loadedChunks.add(chunk); + loadingChunks.remove(k); chunk.onChunkLoad(); chunk.populateChunk(this, this, par1, par2); } diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenShrub.java b/src/main/java/net/minecraft/world/gen/feature/WorldGenShrub.java index 1235e0c..aa180e8 100644 --- a/src/main/java/net/minecraft/world/gen/feature/WorldGenShrub.java +++ b/src/main/java/net/minecraft/world/gen/feature/WorldGenShrub.java @@ -5,6 +5,8 @@ import net.minecraft.block.material.Material; import net.minecraft.init.Blocks; import net.minecraft.world.World; +import net.minecraftforge.common.IPlantable; +import net.minecraftforge.common.util.ForgeDirection; public class WorldGenShrub extends WorldGenTrees { @@ -35,7 +37,7 @@ Block block1 = par1World.getBlock(par3, par4, par5); - if (block1 == Blocks.dirt || block1 == Blocks.grass) + if (block1.canSustainPlant(par1World, par3, par4, par5, ForgeDirection.UP, (IPlantable)Blocks.sapling)) { ++par4; this.setBlockAndNotifyAdequately(par1World, par3, par4, par5, Blocks.log, this.field_150527_b); diff --git a/src/main/java/net/minecraft/world/gen/feature/WorldGenTrees.java b/src/main/java/net/minecraft/world/gen/feature/WorldGenTrees.java index 748231b..a7a7e0b 100644 --- a/src/main/java/net/minecraft/world/gen/feature/WorldGenTrees.java +++ b/src/main/java/net/minecraft/world/gen/feature/WorldGenTrees.java @@ -231,7 +231,7 @@ { --par3; - if (par1World.getBlock(par2, par3, par4).isAir(par1World, par2, par3, par4) || i1 <= 0) + if (!par1World.getBlock(par2, par3, par4).isAir(par1World, par2, par3, par4) || i1 <= 0) { return; } diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayer.java b/src/main/java/net/minecraft/world/gen/layer/GenLayer.java index 1f208a5..2432af0 100644 --- a/src/main/java/net/minecraft/world/gen/layer/GenLayer.java +++ b/src/main/java/net/minecraft/world/gen/layer/GenLayer.java @@ -143,6 +143,22 @@ return j; } + /* ======================================== FORGE START =====================================*/ + protected long nextLong(long par1) + { + long j = (this.chunkSeed >> 24) % par1; + + if (j < 0) + { + j += par1; + } + + this.chunkSeed *= this.chunkSeed * 6364136223846793005L + 1442695040888963407L; + this.chunkSeed += this.worldGenSeed; + return j; + } + /* ========================================= FORGE END ======================================*/ + public abstract int[] getInts(int var1, int var2, int var3, int var4); protected static boolean compareBiomesById(final int p_151616_0_, final int p_151616_1_) @@ -190,7 +206,7 @@ protected static boolean isBiomeOceanic(int p_151618_0_) { - return p_151618_0_ == BiomeGenBase.ocean.biomeID || p_151618_0_ == BiomeGenBase.deepOcean.biomeID || p_151618_0_ == BiomeGenBase.frozenOcean.biomeID; + return BiomeManager.oceanBiomes.contains(BiomeGenBase.getBiome(p_151618_0_)); } protected int selectRandom(int ... p_151619_1_) diff --git a/src/main/java/net/minecraft/world/gen/layer/GenLayerBiome.java b/src/main/java/net/minecraft/world/gen/layer/GenLayerBiome.java index 95a16c0..146d74d 100644 --- a/src/main/java/net/minecraft/world/gen/layer/GenLayerBiome.java +++ b/src/main/java/net/minecraft/world/gen/layer/GenLayerBiome.java @@ -1,28 +1,48 @@ package net.minecraft.world.gen.layer; +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.util.WeightedRandom; import net.minecraft.world.WorldType; import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.common.BiomeManager; +import net.minecraftforge.common.BiomeManager.BiomeEntry; public class GenLayerBiome extends GenLayer { - private BiomeGenBase[] field_151623_c; - private BiomeGenBase[] field_151621_d; - private BiomeGenBase[] field_151622_e; - private BiomeGenBase[] field_151620_f; + private List desertBiomes = new ArrayList(); + private List warmBiomes = new ArrayList(); + private List coolBiomes = new ArrayList(); + private List icyBiomes = new ArrayList(); + private static final String __OBFID = "CL_00000555"; public GenLayerBiome(long par1, GenLayer par3GenLayer, WorldType par4WorldType) { super(par1); - this.field_151623_c = new BiomeGenBase[] {BiomeGenBase.desert, BiomeGenBase.desert, BiomeGenBase.desert, BiomeGenBase.savanna, BiomeGenBase.savanna, BiomeGenBase.plains}; - this.field_151621_d = new BiomeGenBase[] {BiomeGenBase.forest, BiomeGenBase.roofedForest, BiomeGenBase.extremeHills, BiomeGenBase.plains, BiomeGenBase.birchForest, BiomeGenBase.swampland}; - this.field_151622_e = new BiomeGenBase[] {BiomeGenBase.forest, BiomeGenBase.extremeHills, BiomeGenBase.taiga, BiomeGenBase.plains}; - this.field_151620_f = new BiomeGenBase[] {BiomeGenBase.icePlains, BiomeGenBase.icePlains, BiomeGenBase.icePlains, BiomeGenBase.coldTaiga}; + this.parent = par3GenLayer; - + + this.desertBiomes.addAll(BiomeManager.desertBiomes); + this.warmBiomes.addAll(BiomeManager.warmBiomes); + this.coolBiomes.addAll(BiomeManager.coolBiomes); + this.icyBiomes.addAll(BiomeManager.icyBiomes); + if (par4WorldType == WorldType.DEFAULT_1_1) { - this.field_151623_c = new BiomeGenBase[] {BiomeGenBase.desert, BiomeGenBase.forest, BiomeGenBase.extremeHills, BiomeGenBase.swampland, BiomeGenBase.plains, BiomeGenBase.taiga}; + desertBiomes.add(new BiomeEntry(BiomeGenBase.desert, 10)); + desertBiomes.add(new BiomeEntry(BiomeGenBase.forest, 10)); + desertBiomes.add(new BiomeEntry(BiomeGenBase.extremeHills, 10)); + desertBiomes.add(new BiomeEntry(BiomeGenBase.swampland, 10)); + desertBiomes.add(new BiomeEntry(BiomeGenBase.plains, 10)); + desertBiomes.add(new BiomeEntry(BiomeGenBase.taiga, 10)); + } + else + { + desertBiomes.add(new BiomeEntry(BiomeGenBase.desert, 30)); + desertBiomes.add(new BiomeEntry(BiomeGenBase.savanna, 20)); + desertBiomes.add(new BiomeEntry(BiomeGenBase.plains, 10)); } } @@ -63,7 +83,7 @@ } else { - aint1[j1 + i1 * par3] = this.field_151623_c[this.nextInt(this.field_151623_c.length)].biomeID; + aint1[j1 + i1 * par3] = ((BiomeEntry)WeightedRandom.getItem(this.desertBiomes, (int)(this.nextLong(WeightedRandom.getTotalWeight(this.desertBiomes) / 10) * 10))).biome.biomeID; } } else if (k1 == 2) @@ -74,7 +94,7 @@ } else { - aint1[j1 + i1 * par3] = this.field_151621_d[this.nextInt(this.field_151621_d.length)].biomeID; + aint1[j1 + i1 * par3] = ((BiomeEntry)WeightedRandom.getItem(this.warmBiomes, (int)(this.nextLong(WeightedRandom.getTotalWeight(this.warmBiomes) / 10) * 10))).biome.biomeID; } } else if (k1 == 3) @@ -85,12 +105,12 @@ } else { - aint1[j1 + i1 * par3] = this.field_151622_e[this.nextInt(this.field_151622_e.length)].biomeID; + aint1[j1 + i1 * par3] = ((BiomeEntry)WeightedRandom.getItem(this.coolBiomes, (int)(this.nextLong(WeightedRandom.getTotalWeight(this.coolBiomes) / 10) * 10))).biome.biomeID; } } else if (k1 == 4) { - aint1[j1 + i1 * par3] = this.field_151620_f[this.nextInt(this.field_151620_f.length)].biomeID; + aint1[j1 + i1 * par3] = ((BiomeEntry)WeightedRandom.getItem(this.icyBiomes, (int)(this.nextLong(WeightedRandom.getTotalWeight(this.icyBiomes) / 10) * 10))).biome.biomeID; } else { diff --git a/src/main/java/net/minecraftforge/client/EnumHelperClient.java b/src/main/java/net/minecraftforge/client/EnumHelperClient.java index f991fdd..2f452b2 100644 --- a/src/main/java/net/minecraftforge/client/EnumHelperClient.java +++ b/src/main/java/net/minecraftforge/client/EnumHelperClient.java @@ -13,8 +13,7 @@ { {GameType.class, int.class, String.class}, {Options.class, String.class, boolean.class, boolean.class}, - {EnumOS.class}, - {EnumRarity.class, int.class, String.class} + {EnumOS.class} }; public static GameType addGameType(String name, int id, String displayName) @@ -31,11 +30,6 @@ { return addEnum(EnumOS.class, name); } - - public static EnumRarity addRarity(String name, int color, String displayName) - { - return addEnum(EnumRarity.class, name, color, displayName); - } public static > T addEnum(Class enumType, String enumName, Object... paramValues) { diff --git a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java index fc13f8d..5302991 100644 --- a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java +++ b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java @@ -35,6 +35,7 @@ import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; +import net.minecraft.world.ChunkCache; import net.minecraft.world.World; import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.client.model.ModelBiped; @@ -42,13 +43,16 @@ import net.minecraft.client.renderer.RenderBlocks; import net.minecraft.client.renderer.RenderGlobal; import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.client.renderer.entity.RenderItem; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.resources.I18n; import net.minecraft.client.settings.GameSettings; import net.minecraftforge.client.IItemRenderer.ItemRenderType; import net.minecraftforge.client.event.DrawBlockHighlightEvent; import net.minecraftforge.client.event.RenderHandEvent; +import net.minecraftforge.client.event.RenderWorldEvent; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.event.sound.PlaySoundEvent17; @@ -422,9 +426,9 @@ if (status == BETA || status == BETA_OUTDATED) { // render a warning at the top of the screen, - String line = EnumChatFormatting.RED + "WARNING:" + EnumChatFormatting.RESET + " Forge Beta,"; + String line = I18n.format("forge.update.beta.1", EnumChatFormatting.RED, EnumChatFormatting.RESET); gui.drawString(font, line, (width - font.getStringWidth(line)) / 2, 4 + (0 * (font.FONT_HEIGHT + 1)), -1); - line = "Major issues may arise, verify before reporting."; + line = I18n.format("forge.update.beta.2"); gui.drawString(font, line, (width - font.getStringWidth(line)) / 2, 4 + (1 * (font.FONT_HEIGHT + 1)), -1); } @@ -435,7 +439,7 @@ //case UP_TO_DATE: line = "Forge up to date"}; break; //case AHEAD: line = "Using non-recommended Forge build, issues may arise."}; break; case OUTDATED: - case BETA_OUTDATED: line = "New Forge version available: " + ForgeVersion.getTarget(); break; + case BETA_OUTDATED: line = I18n.format("forge.update.newversion", ForgeVersion.getTarget()); break; default: break; } @@ -453,4 +457,35 @@ MinecraftForge.EVENT_BUS.post(e); return e.result; } + + static RenderBlocks worldRendererRB; + static int worldRenderPass; + + public static int getWorldRenderPass() + { + return worldRenderPass; + } + + public static void setWorldRendererRB(RenderBlocks renderBlocks) + { + worldRendererRB = renderBlocks; + } + + public static void onPreRenderWorld(WorldRenderer worldRenderer, int pass) + { + if(worldRendererRB != null) + { + worldRenderPass = pass; + MinecraftForge.EVENT_BUS.post(new RenderWorldEvent.Pre(worldRenderer, (ChunkCache)worldRendererRB.blockAccess, worldRendererRB, pass)); + } + } + + public static void onPostRenderWorld(WorldRenderer worldRenderer, int pass) + { + if(worldRendererRB != null) + { + MinecraftForge.EVENT_BUS.post(new RenderWorldEvent.Post(worldRenderer, (ChunkCache)worldRendererRB.blockAccess, worldRendererRB, pass)); + worldRenderPass = -1; + } + } } diff --git a/src/main/java/net/minecraftforge/client/event/EntityViewRenderEvent.java b/src/main/java/net/minecraftforge/client/event/EntityViewRenderEvent.java new file mode 100644 index 0000000..18dbc1f --- /dev/null +++ b/src/main/java/net/minecraftforge/client/event/EntityViewRenderEvent.java @@ -0,0 +1,62 @@ +package net.minecraftforge.client.event; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.EntityRenderer; +import net.minecraft.entity.EntityLivingBase; +import cpw.mods.fml.common.eventhandler.Cancelable; +import cpw.mods.fml.common.eventhandler.Event; + +/** + * Event that hooks into EntityRenderer, allowing any feature to customize visual attributes of + * fog the player sees. + */ +public abstract class EntityViewRenderEvent extends Event +{ + public final EntityRenderer renderer; + public final EntityLivingBase entity; + public final Block block; + public final double renderPartialTicks; + + public EntityViewRenderEvent(EntityRenderer renderer, EntityLivingBase entity, Block block, double renderPartialTicks) + { + this.renderer = renderer; + this.entity = entity; + this.block = block; + this.renderPartialTicks = renderPartialTicks; + } + + /** + * Event that allows any feature to customize the fog density the player sees. + * NOTE: In order to make this event have an effect, you must cancel the event + */ + @Cancelable + public static class FogDensity extends EntityViewRenderEvent + { + public float density; + + public FogDensity(EntityRenderer renderer, EntityLivingBase entity, Block block, double renderPartialTicks, float density) + { + super(renderer, entity, block, renderPartialTicks); + this.density = density; + } + } + + /** + * Event that allows any feature to customize the color of fog the player sees. + * NOTE: Any change made to one of the color variables will affect the result seen in-game. + */ + public static class FogColors extends EntityViewRenderEvent + { + public float red; + public float green; + public float blue; + + public FogColors(EntityRenderer renderer, EntityLivingBase entity, Block block, double renderPartialTicks, float red, float green, float blue) + { + super(renderer, entity, block, renderPartialTicks); + this.red = red; + this.green = green; + this.blue = blue; + } + } +} diff --git a/src/main/java/net/minecraftforge/client/event/RenderWorldEvent.java b/src/main/java/net/minecraftforge/client/event/RenderWorldEvent.java new file mode 100644 index 0000000..43ed9ae --- /dev/null +++ b/src/main/java/net/minecraftforge/client/event/RenderWorldEvent.java @@ -0,0 +1,39 @@ +package net.minecraftforge.client.event; + +import cpw.mods.fml.common.eventhandler.Event; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.world.ChunkCache; + +public abstract class RenderWorldEvent extends Event +{ + public final WorldRenderer renderer; + public final ChunkCache chunkCache; + public final RenderBlocks renderBlocks; + public final int pass; + + public RenderWorldEvent(WorldRenderer renderer, ChunkCache chunkCache, RenderBlocks renderBlocks, int pass) + { + this.renderer = renderer; + this.chunkCache = chunkCache; + this.renderBlocks = renderBlocks; + this.pass = pass; + } + + /** + * Fired when 16x16x16 chunk area is being redrawn. + * Fired after GL state is setup, before tessellator is started. + */ + public static class Pre extends RenderWorldEvent + { + public Pre(WorldRenderer renderer, ChunkCache chunkCache, RenderBlocks renderBlocks, int pass){ super(renderer, chunkCache, renderBlocks, pass); } + } + + /** + * Fired after the tessellator is stopped, before the display list is ended. + */ + public static class Post extends RenderWorldEvent + { + public Post(WorldRenderer renderer, ChunkCache chunkCache, RenderBlocks renderBlocks, int pass){ super(renderer, chunkCache, renderBlocks, pass); } + } +} diff --git a/src/main/java/net/minecraftforge/common/BiomeManager.java b/src/main/java/net/minecraftforge/common/BiomeManager.java index b88c20c..1a99ac5 100644 --- a/src/main/java/net/minecraftforge/common/BiomeManager.java +++ b/src/main/java/net/minecraftforge/common/BiomeManager.java @@ -1,16 +1,50 @@ package net.minecraftforge.common; import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import net.minecraft.util.WeightedRandom; +import net.minecraft.world.WorldType; import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.world.gen.structure.MapGenVillage; import net.minecraft.world.biome.WorldChunkManager; public class BiomeManager { + public static List desertBiomes = new ArrayList(); + public static List warmBiomes = new ArrayList(); + public static List coolBiomes = new ArrayList(); + public static List icyBiomes = new ArrayList(); + + public static List oceanBiomes = new ArrayList(); + public static ArrayList strongHoldBiomes = new ArrayList(); public static ArrayList strongHoldBiomesBlackList = new ArrayList(); + static + { + warmBiomes.add(new BiomeEntry(BiomeGenBase.forest, 10)); + warmBiomes.add(new BiomeEntry(BiomeGenBase.roofedForest, 10)); + warmBiomes.add(new BiomeEntry(BiomeGenBase.extremeHills, 10)); + warmBiomes.add(new BiomeEntry(BiomeGenBase.plains, 10)); + warmBiomes.add(new BiomeEntry(BiomeGenBase.birchForest, 10)); + warmBiomes.add(new BiomeEntry(BiomeGenBase.swampland, 10)); + + coolBiomes.add(new BiomeEntry(BiomeGenBase.forest, 10)); + coolBiomes.add(new BiomeEntry(BiomeGenBase.extremeHills, 10)); + coolBiomes.add(new BiomeEntry(BiomeGenBase.taiga, 10)); + coolBiomes.add(new BiomeEntry(BiomeGenBase.plains, 10)); + + icyBiomes.add(new BiomeEntry(BiomeGenBase.icePlains, 30)); + icyBiomes.add(new BiomeEntry(BiomeGenBase.coldTaiga, 10)); + + oceanBiomes.add(BiomeGenBase.ocean); + oceanBiomes.add(BiomeGenBase.deepOcean); + oceanBiomes.add(BiomeGenBase.frozenOcean); + } + @SuppressWarnings("unchecked") public static void addVillageBiome(BiomeGenBase biome, boolean canSpawn) { @@ -64,4 +98,16 @@ WorldChunkManager.allowedBiomes.remove(biome); } } + + public static class BiomeEntry extends WeightedRandom.Item + { + public final BiomeGenBase biome; + + public BiomeEntry(BiomeGenBase biome, int weight) + { + super(weight); + + this.biome = biome; + } + } } \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/DimensionManager.java b/src/main/java/net/minecraftforge/common/DimensionManager.java index bb8cae2..e2bcf83 100644 --- a/src/main/java/net/minecraftforge/common/DimensionManager.java +++ b/src/main/java/net/minecraftforge/common/DimensionManager.java @@ -380,9 +380,9 @@ public static void loadDimensionDataMap(NBTTagCompound compoundTag) { + dimensionMap.clear(); if (compoundTag == null) { - dimensionMap.clear(); for (Integer id : dimensions.keySet()) { if (id >= 0) diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 188145d..47704fe 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; + import cpw.mods.fml.common.eventhandler.Event; import cpw.mods.fml.relauncher.ReflectionHelper; import net.minecraft.block.Block; @@ -25,6 +26,7 @@ import net.minecraft.network.Packet; import net.minecraft.network.play.server.S23PacketBlockChange; import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityNote; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.DamageSource; @@ -47,6 +49,7 @@ import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; import net.minecraftforge.event.entity.player.PlayerOpenContainerEvent; import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.NoteBlockEvent; import static net.minecraft.init.Blocks.*; public class ForgeHooks @@ -436,4 +439,16 @@ container.maximumCost = e.cost; return false; } + + public static boolean onNoteChange(TileEntityNote te, byte old) + { + NoteBlockEvent.Change e = new NoteBlockEvent.Change(te.getWorldObj(), te.xCoord, te.yCoord, te.zCoord, te.getBlockMetadata(), old, te.note); + if (MinecraftForge.EVENT_BUS.post(e)) + { + te.note = old; + return false; + } + te.note = (byte)e.getVanillaNoteId(); + return true; + } } diff --git a/src/main/java/net/minecraftforge/common/ForgeModContainer.java b/src/main/java/net/minecraftforge/common/ForgeModContainer.java index cc6cbec..a80f2e0 100644 --- a/src/main/java/net/minecraftforge/common/ForgeModContainer.java +++ b/src/main/java/net/minecraftforge/common/ForgeModContainer.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; + import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; @@ -19,6 +20,7 @@ import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; import net.minecraftforge.common.network.ForgeNetworkHandler; +import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.oredict.RecipeSorter; import net.minecraftforge.server.command.ForgeCommand; @@ -179,7 +181,7 @@ @Subscribe public void modConstruction(FMLConstructionEvent evt) { - NetworkRegistry.INSTANCE.register(this, this.getClass(), null, evt.getASMHarvestedData()); + NetworkRegistry.INSTANCE.register(this, this.getClass(), "*", evt.getASMHarvestedData()); ForgeNetworkHandler.registerChannel(this, evt.getSide()); } @@ -223,16 +225,14 @@ @Override public void readData(SaveHandler handler, WorldInfo info, Map propertyMap, NBTTagCompound tag) { - if (tag.hasKey("DimensionData")) - { - DimensionManager.loadDimensionDataMap(tag.hasKey("DimensionData") ? tag.getCompoundTag("DimensionData") : null); - } + DimensionManager.loadDimensionDataMap(tag.hasKey("DimensionData") ? tag.getCompoundTag("DimensionData") : null); } @Subscribe public void mappingChanged(FMLModIdMappingEvent evt) { Blocks.fire.rebuildFireInfo(); + OreDictionary.rebakeMap(); } diff --git a/src/main/java/net/minecraftforge/common/ForgeVersion.java b/src/main/java/net/minecraftforge/common/ForgeVersion.java index 7a87922..95c10a4 100644 --- a/src/main/java/net/minecraftforge/common/ForgeVersion.java +++ b/src/main/java/net/minecraftforge/common/ForgeVersion.java @@ -23,7 +23,7 @@ //This number is incremented every minecraft release, never reset public static final int minorVersion = 12; //This number is incremented every time a interface changes or new major feature is added, and reset every Minecraft version - public static final int revisionVersion = 1; + public static final int revisionVersion = 2; //This number is incremented every time Jenkins builds Forge, and never reset. Should always be 0 in the repo code. public static final int buildVersion = 0; diff --git a/src/main/java/net/minecraftforge/common/IShearable.java b/src/main/java/net/minecraftforge/common/IShearable.java index 7e40580..89abbd2 100644 --- a/src/main/java/net/minecraftforge/common/IShearable.java +++ b/src/main/java/net/minecraftforge/common/IShearable.java @@ -32,7 +32,8 @@ * Performs the shear function on this object. * This is called for both client, and server. * The object should perform all actions related to being sheared, - * except for dropping of the items. + * except for dropping of the items, and removal of the block. + * As those are handled by ItemShears itself. * * Returns a list of items that resulted from the shearing process. * diff --git a/src/main/java/net/minecraftforge/common/chunkio/ChunkIOExecutor.java b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOExecutor.java new file mode 100644 index 0000000..2ecea4e --- /dev/null +++ b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOExecutor.java @@ -0,0 +1,32 @@ +package net.minecraftforge.common.chunkio; + +import net.minecraftforge.common.util.AsynchronousExecutor; + +public class ChunkIOExecutor { + static final int BASE_THREADS = 1; + static final int PLAYERS_PER_THREAD = 50; + + private static final AsynchronousExecutor instance = new AsynchronousExecutor(new ChunkIOProvider(), BASE_THREADS); + + public static net.minecraft.world.chunk.Chunk syncChunkLoad(net.minecraft.world.World world, net.minecraft.world.chunk.storage.AnvilChunkLoader loader, net.minecraft.world.gen.ChunkProviderServer provider, int x, int z) { + return instance.getSkipQueue(new QueuedChunk(x, z, loader, world, provider)); + } + + public static void queueChunkLoad(net.minecraft.world.World world, net.minecraft.world.chunk.storage.AnvilChunkLoader loader, net.minecraft.world.gen.ChunkProviderServer provider, int x, int z, Runnable runnable) { + instance.add(new QueuedChunk(x, z, loader, world, provider), runnable); + } + + // Abuses the fact that hashCode and equals for QueuedChunk only use world and coords + public static void dropQueuedChunkLoad(net.minecraft.world.World world, int x, int z, Runnable runnable) { + instance.drop(new QueuedChunk(x, z, null, world, null), runnable); + } + + public static void adjustPoolSize(int players) { + int size = Math.max(BASE_THREADS, (int) Math.ceil(players / PLAYERS_PER_THREAD)); + instance.setActiveThreads(size); + } + + public static void tick() { + instance.finishActive(); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java new file mode 100644 index 0000000..eb962d2 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java @@ -0,0 +1,64 @@ +package net.minecraftforge.common.chunkio; + + +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.AsynchronousExecutor; +import net.minecraftforge.event.world.ChunkDataEvent; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider { + private final AtomicInteger threadNumber = new AtomicInteger(1); + + // async stuff + public net.minecraft.world.chunk.Chunk callStage1(QueuedChunk queuedChunk) throws RuntimeException { + net.minecraft.world.chunk.storage.AnvilChunkLoader loader = queuedChunk.loader; + Object[] data = null; + try { + data = loader.loadChunk__Async(queuedChunk.world, queuedChunk.x, queuedChunk.z); + } catch (IOException e) { + e.printStackTrace(); + } + + if (data != null) { + queuedChunk.compound = (net.minecraft.nbt.NBTTagCompound) data[1]; + return (net.minecraft.world.chunk.Chunk) data[0]; + } + + return null; + } + + // sync stuff + public void callStage2(QueuedChunk queuedChunk, net.minecraft.world.chunk.Chunk chunk) throws RuntimeException { + if(chunk == null) { + // If the chunk loading failed just do it synchronously (may generate) + queuedChunk.provider.loadChunk(queuedChunk.x, queuedChunk.z); + return; + } + + queuedChunk.loader.loadEntities(queuedChunk.world, queuedChunk.compound.getCompoundTag("Level"), chunk); + MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, queuedChunk.compound)); // Don't call ChunkDataEvent.Load async + chunk.lastSaveTime = queuedChunk.provider.worldObj.getTotalWorldTime(); + queuedChunk.provider.loadedChunkHashMap.add(ChunkCoordIntPair.chunkXZ2Int(queuedChunk.x, queuedChunk.z), chunk); + queuedChunk.provider.loadedChunks.add(chunk); + chunk.onChunkLoad(); + + if (queuedChunk.provider.currentChunkProvider != null) { + queuedChunk.provider.currentChunkProvider.recreateStructures(queuedChunk.x, queuedChunk.z); + } + + chunk.populateChunk(queuedChunk.provider, queuedChunk.provider, queuedChunk.x, queuedChunk.z); + } + + public void callStage3(QueuedChunk queuedChunk, net.minecraft.world.chunk.Chunk chunk, Runnable runnable) throws RuntimeException { + runnable.run(); + } + + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable, "Chunk I/O Executor Thread-" + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/chunkio/QueuedChunk.java b/src/main/java/net/minecraftforge/common/chunkio/QueuedChunk.java new file mode 100644 index 0000000..40e3592 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/chunkio/QueuedChunk.java @@ -0,0 +1,52 @@ +package net.minecraftforge.common.chunkio; + + +class QueuedChunk { + final int x; + final int z; + final net.minecraft.world.chunk.storage.AnvilChunkLoader loader; + final net.minecraft.world.World world; + final net.minecraft.world.gen.ChunkProviderServer provider; + net.minecraft.nbt.NBTTagCompound compound; + + public QueuedChunk(int x, int z, net.minecraft.world.chunk.storage.AnvilChunkLoader loader, net.minecraft.world.World world, net.minecraft.world.gen.ChunkProviderServer provider) { + this.x = x; + this.z = z; + this.loader = loader; + this.world = world; + this.provider = provider; + } + + @Override + public int hashCode() { + return (x * 31 + z * 29) ^ world.hashCode(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof QueuedChunk) { + QueuedChunk other = (QueuedChunk) object; + return x == other.x && z == other.z && world == other.world; + } + + return false; + } + + @Override + public String toString() + { + StringBuilder result = new StringBuilder(); + String NEW_LINE = System.getProperty("line.separator"); + + result.append(this.getClass().getName() + " {" + NEW_LINE); + result.append(" x: " + x + NEW_LINE); + result.append(" z: " + z + NEW_LINE); + result.append(" loader: " + loader + NEW_LINE ); + result.append(" world: " + world.getWorldInfo().getWorldName() + NEW_LINE); + result.append(" dimension: " + world.provider.dimensionId + NEW_LINE); + result.append(" provider: " + world.provider.getClass().getName() + NEW_LINE); + result.append("}"); + + return result.toString(); + } +} diff --git a/src/main/java/net/minecraftforge/common/util/AsynchronousExecutor.java b/src/main/java/net/minecraftforge/common/util/AsynchronousExecutor.java new file mode 100644 index 0000000..a7ca10a --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/AsynchronousExecutor.java @@ -0,0 +1,361 @@ +package net.minecraftforge.common.util; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +import cpw.mods.fml.common.FMLLog; + + +/** + * Executes tasks using a multi-stage process executor. Synchronous executions are via {@link AsynchronousExecutor#finishActive()} or the {@link AsynchronousExecutor#get(Object)} methods. + *
  • Stage 1 creates the object from a parameter, and is usually called asynchronously. + *
  • Stage 2 takes the parameter and object from stage 1 and does any synchronous processing to prepare it. + *
  • Stage 3 takes the parameter and object from stage 1, as well as a callback that was registered, and performs any synchronous calculations. + * + * @param

    The type of parameter you provide to make the object that will be created. It should implement {@link Object#hashCode()} and {@link Object#equals(Object)} if you want to get the value early. + * @param The type of object you provide. This is created in stage 1, and passed to stage 2, 3, and returned if get() is called. + * @param The type of callback you provide. You may register many of these to be passed to the provider in stage 3, one at a time. + * @param A type of exception you may throw and expect to be handled by the main thread + * @author Wesley Wolfe (c) 2012, 2014 + */ +public final class AsynchronousExecutor { + + public static interface CallBackProvider extends ThreadFactory { + + /** + * Normally an asynchronous call, but can be synchronous + * + * @param parameter parameter object provided + * @return the created object + */ + T callStage1(P parameter) throws E; + + /** + * Synchronous call + * + * @param parameter parameter object provided + * @param object the previously created object + */ + void callStage2(P parameter, T object) throws E; + + /** + * Synchronous call, called multiple times, once per registered callback + * + * @param parameter parameter object provided + * @param object the previously created object + * @param callback the current callback to execute + */ + void callStage3(P parameter, T object, C callback) throws E; + } + + @SuppressWarnings("rawtypes") + static final AtomicIntegerFieldUpdater STATE_FIELD = AtomicIntegerFieldUpdater.newUpdater(AsynchronousExecutor.Task.class, "state"); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static boolean set(AsynchronousExecutor.Task $this, int expected, int value) { + return STATE_FIELD.compareAndSet($this, expected, value); + } + + class Task implements Runnable { + static final int PENDING = 0x0; + static final int STAGE_1_ASYNC = PENDING + 1; + static final int STAGE_1_SYNC = STAGE_1_ASYNC + 1; + static final int STAGE_1_COMPLETE = STAGE_1_SYNC + 1; + static final int FINISHED = STAGE_1_COMPLETE + 1; + + volatile int state = PENDING; + final P parameter; + T object; + final List callbacks = new LinkedList(); + E t = null; + + Task(final P parameter) { + this.parameter = parameter; + } + + public void run() { + if (initAsync()) { + finished.add(this); + } + } + + boolean initAsync() { + if (set(this, PENDING, STAGE_1_ASYNC)) { + boolean ret = true; + + try { + init(); + } finally { + if (set(this, STAGE_1_ASYNC, STAGE_1_COMPLETE)) { + // No one is/will be waiting + } else { + // We know that the sync thread will be waiting + synchronized (this) { + if (state != STAGE_1_SYNC) { + // They beat us to the synchronized block + this.notifyAll(); + } else { + // We beat them to the synchronized block + } + state = STAGE_1_COMPLETE; // They're already synchronized, atomic locks are not needed + } + // We want to return false, because we know a synchronous task already handled the finish() + ret = false; // Don't return inside finally; VERY bad practice. + } + } + + return ret; + } else { + return false; + } + } + + void initSync() { + if (set(this, PENDING, STAGE_1_COMPLETE)) { + // If we succeed that variable switch, good as done + init(); + } else if (set(this, STAGE_1_ASYNC, STAGE_1_SYNC)) { + // Async thread is running, but this shouldn't be likely; we need to sync to wait on them because of it. + synchronized (this) { + if (set(this, STAGE_1_SYNC, PENDING)) { // They might NOT synchronized yet, atomic lock IS needed + // We are the first into the lock + while (state != STAGE_1_COMPLETE) { + try { + this.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unable to handle interruption on " + parameter, e); + } + } + } else { + // They beat us to the synchronized block + } + } + } else { + // Async thread is not pending, the more likely situation for a task not pending + } + } + + @SuppressWarnings("unchecked") + void init() { + try { + object = provider.callStage1(parameter); + } catch (final Throwable t) { + this.t = (E) t; + } + } + + @SuppressWarnings("unchecked") + T get() throws E { + initSync(); + if (callbacks.isEmpty()) { + // 'this' is a placeholder to prevent callbacks from being empty during finish call + // See get method below + callbacks.add((C) this); + } + finish(); + return object; + } + + void finish() throws E { + switch (state) { + default: + case PENDING: + case STAGE_1_ASYNC: + case STAGE_1_SYNC: + throw new IllegalStateException("Attempting to finish unprepared(" + state + ") task(" + parameter + ")"); + case STAGE_1_COMPLETE: + try { + if (t != null) { + throw t; + } + if (callbacks.isEmpty()) { + return; + } + + final CallBackProvider provider = AsynchronousExecutor.this.provider; + final P parameter = this.parameter; + final T object = this.object; + + provider.callStage2(parameter, object); + for (C callback : callbacks) { + if (callback == this) { + // 'this' is a placeholder to prevent callbacks from being empty on a get() call + // See get method above + continue; + } + provider.callStage3(parameter, object, callback); + } + } finally { + tasks.remove(parameter); + state = FINISHED; + } + case FINISHED: + } + } + + boolean drop() { + if (set(this, PENDING, FINISHED)) { + // If we succeed that variable switch, good as forgotten + tasks.remove(parameter); + return true; + } else { + // We need the async thread to finish normally to properly dispose of the task + return false; + } + } + } + + final CallBackProvider provider; + final Queue finished = new ConcurrentLinkedQueue(); + final Map tasks = new HashMap(); + final ThreadPoolExecutor pool; + + /** + * Uses a thread pool to pass executions to the provider. + * @see AsynchronousExecutor + */ + public AsynchronousExecutor(final CallBackProvider provider, final int coreSize) { + if (provider == null) { + throw new IllegalArgumentException("Provider cannot be null"); + } + this.provider = provider; + + // We have an unbound queue size so do not need a max thread size + pool = new ThreadPoolExecutor(coreSize, Integer.MAX_VALUE, 60l, TimeUnit.SECONDS, new LinkedBlockingQueue(), provider); + } + + /** + * Adds a callback to the parameter provided, adding parameter to the queue if needed. + *

    + * This should always be synchronous. + */ + public void add(P parameter, C callback) { + Task task = tasks.get(parameter); + if (task == null) { + tasks.put(parameter, task = new Task(parameter)); + pool.execute(task); + } + task.callbacks.add(callback); + } + + /** + * This removes a particular callback from the specified parameter. + *

    + * If no callbacks remain for a given parameter, then the {@link CallBackProvider CallBackProvider's} stages may be omitted from execution. + * Stage 3 will have no callbacks, stage 2 will be skipped unless a {@link #get(Object)} is used, and stage 1 will be avoided on a best-effort basis. + *

    + * Subsequent calls to {@link #getSkipQueue(Object)} will always work. + *

    + * Subsequent calls to {@link #get(Object)} might work. + *

    + * This should always be synchronous + * @return true if no further execution for the parameter is possible, such that, no exceptions will be thrown in {@link #finishActive()} for the parameter, and {@link #get(Object)} will throw an {@link IllegalStateException}, false otherwise + * @throws IllegalStateException if parameter is not in the queue anymore + * @throws IllegalStateException if the callback was not specified for given parameter + */ + public boolean drop(P parameter, C callback) throws IllegalStateException { + final Task task = tasks.get(parameter); + if (task == null) { + // Print debug info for QueuedChunk and avoid crash + //throw new IllegalStateException("Unknown " + parameter); + FMLLog.info("Unknown %s", parameter); + FMLLog.info("This should not happen. Please report this error to Forge."); + return false; + } + if (!task.callbacks.remove(callback)) { + throw new IllegalStateException("Unknown " + callback + " for " + parameter); + } + if (task.callbacks.isEmpty()) { + return task.drop(); + } + return false; + } + + /** + * This method attempts to skip the waiting period for said parameter. + *

    + * This should always be synchronous. + * @throws IllegalStateException if the parameter is not in the queue anymore, or sometimes if called from asynchronous thread + */ + public T get(P parameter) throws E, IllegalStateException { + final Task task = tasks.get(parameter); + if (task == null) { + throw new IllegalStateException("Unknown " + parameter); + } + return task.get(); + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter) throws E { + return skipQueue(parameter); + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, C callback) throws E { + final T object = skipQueue(parameter); + provider.callStage3(parameter, object, callback); + return object; + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, C...callbacks) throws E { + final CallBackProvider provider = this.provider; + final T object = skipQueue(parameter); + for (C callback : callbacks) { + provider.callStage3(parameter, object, callback); + } + return object; + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, Iterable callbacks) throws E { + final CallBackProvider provider = this.provider; + final T object = skipQueue(parameter); + for (C callback : callbacks) { + provider.callStage3(parameter, object, callback); + } + return object; + } + + private T skipQueue(P parameter) throws E { + Task task = tasks.get(parameter); + if (task != null) { + return task.get(); + } + T object = provider.callStage1(parameter); + provider.callStage2(parameter, object); + return object; + } + + /** + * This is the 'heartbeat' that should be called synchronously to finish any pending tasks + */ + public void finishActive() throws E { + final Queue finished = this.finished; + while (!finished.isEmpty()) { + finished.poll().finish(); + } + } + + public void setActiveThreads(final int coreSize) { + pool.setCorePoolSize(coreSize); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/util/ChunkCoordComparator.java b/src/main/java/net/minecraftforge/common/util/ChunkCoordComparator.java new file mode 100644 index 0000000..a170dd5 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/ChunkCoordComparator.java @@ -0,0 +1,60 @@ +package net.minecraftforge.common.util; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.ChunkCoordIntPair; + +// Sorter to load nearby chunks first +public class ChunkCoordComparator implements java.util.Comparator +{ + private int x; + private int z; + + public ChunkCoordComparator(EntityPlayerMP entityplayer) + { + x = (int) entityplayer.posX >> 4; + z = (int) entityplayer.posZ >> 4; + } + + public int compare(ChunkCoordIntPair a, ChunkCoordIntPair b) + { + if (a.equals(b)) + { + return 0; + } + + // Subtract current position to set center point + int ax = a.chunkXPos - this.x; + int az = a.chunkZPos - this.z; + int bx = b.chunkXPos - this.x; + int bz = b.chunkZPos - this.z; + int result = ((ax - bx) * (ax + bx)) + ((az - bz) * (az + bz)); + + if (result != 0) + { + return result; + } + + if (ax < 0) + { + if (bx < 0) + { + return bz - az; + } + else + { + return -1; + } + } + else + { + if (bx < 0) + { + return 1; + } + else + { + return az - bz; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/util/EnumHelper.java b/src/main/java/net/minecraftforge/common/util/EnumHelper.java index 8d40292..dc0e1a7 100644 --- a/src/main/java/net/minecraftforge/common/util/EnumHelper.java +++ b/src/main/java/net/minecraftforge/common/util/EnumHelper.java @@ -13,8 +13,10 @@ import net.minecraft.entity.item.EntityPainting.EnumArt; import net.minecraft.entity.player.EntityPlayer.EnumStatus; import net.minecraft.item.EnumAction; +import net.minecraft.item.EnumRarity; import net.minecraft.item.Item.ToolMaterial; import net.minecraft.item.ItemArmor.ArmorMaterial; +import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.gen.structure.StructureStrongholdPieces.Stronghold.Door; @@ -37,7 +39,7 @@ {ArmorMaterial.class, int.class, int[].class, int.class}, {EnumArt.class, String.class, int.class, int.class, int.class, int.class}, {EnumCreatureAttribute.class}, - {EnumCreatureType.class, Class.class, int.class, Material.class, boolean.class}, + {EnumCreatureType.class, Class.class, int.class, Material.class, boolean.class, boolean.class}, {Door.class}, {EnumEnchantmentType.class}, {EnumEntitySize.class}, @@ -45,7 +47,8 @@ {MovingObjectType.class}, {EnumSkyBlock.class, int.class}, {EnumStatus.class}, - {ToolMaterial.class, int.class, int.class, float.class, float.class, int.class} + {ToolMaterial.class, int.class, int.class, float.class, float.class, int.class}, + {EnumRarity.class, EnumChatFormatting.class, String.class} }; public static EnumAction addAction(String name) @@ -65,9 +68,9 @@ return addEnum(EnumCreatureAttribute.class, name); } @SuppressWarnings("rawtypes") - public static EnumCreatureType addCreatureType(String name, Class typeClass, int maxNumber, Material material, boolean peaceful) + public static EnumCreatureType addCreatureType(String name, Class typeClass, int maxNumber, Material material, boolean peaceful, boolean animal) { - return addEnum(EnumCreatureType.class, name, typeClass, maxNumber, material, peaceful); + return addEnum(EnumCreatureType.class, name, typeClass, maxNumber, material, peaceful, animal); } public static Door addDoor(String name) { @@ -101,6 +104,10 @@ { return addEnum(ToolMaterial.class, name, harvestLevel, maxUses, efficiency, damage, enchantability); } + public static EnumRarity addRarity(String name, EnumChatFormatting color, String displayName) + { + return addEnum(EnumRarity.class, name, color, displayName); + } private static void setup() { diff --git a/src/main/java/net/minecraftforge/common/util/RotationHelper.java b/src/main/java/net/minecraftforge/common/util/RotationHelper.java index 5fa8d89..ebc8e4a 100644 --- a/src/main/java/net/minecraftforge/common/util/RotationHelper.java +++ b/src/main/java/net/minecraftforge/common/util/RotationHelper.java @@ -42,6 +42,11 @@ import static net.minecraftforge.common.util.ForgeDirection.*; +/** + * This class is a helper function for vanilla blocks, and should not be called by Modders. + * Refer to block.rotateBlock and block.getValidRotations instead. + * + */ public class RotationHelper { /** * Some blocks have the same rotation. diff --git a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java index c17165e..0660f99 100644 --- a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java +++ b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java @@ -51,9 +51,14 @@ return (MinecraftForge.EVENT_BUS.post(event) ? -1 : event.newSpeed); } + @Deprecated public static PlayerInteractEvent onPlayerInteract(EntityPlayer player, Action action, int x, int y, int z, int face) { - PlayerInteractEvent event = new PlayerInteractEvent(player, action, x, y, z, face); + return onPlayerInteract(player, action, x, y, z, face, null); + } + public static PlayerInteractEvent onPlayerInteract(EntityPlayer player, Action action, int x, int y, int z, int face, World world) + { + PlayerInteractEvent event = new PlayerInteractEvent(player, action, x, y, z, face, world); MinecraftForge.EVENT_BUS.post(event); return event; } @@ -155,4 +160,14 @@ MinecraftForge.EVENT_BUS.post(event); return event.result; } + + public static void onStartEntityTracking(Entity entity, EntityPlayer player) + { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.StartTracking(player, entity)); + } + + public static void onStopEntityTracking(Entity entity, EntityPlayer player) + { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.StopTracking(player, entity)); + } } diff --git a/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java b/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java index fbcf587..065ad1e 100644 --- a/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java +++ b/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java @@ -2,6 +2,7 @@ import cpw.mods.fml.common.eventhandler.Cancelable; import net.minecraft.block.Block; +import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraftforge.event.entity.living.LivingEvent; @@ -68,4 +69,66 @@ this.displayname = username; } } + + /** + * Fired when the EntityPlayer is cloned, typically caused by the network sending a RESPAWN_PLAYER event. + * Either caused by death, or by traveling from the End to the overworld. + */ + public static class Clone extends PlayerEvent + { + /** + * The old EntityPlayer that this new entity is a clone of. + */ + public final EntityPlayer original; + /** + * True if this event was fired because the player died. + * False if it was fired because the entity switched dimensions. + */ + public final boolean wasDeath; + + public Clone(EntityPlayer _new, EntityPlayer oldPlayer, boolean wasDeath) + { + super(_new); + this.original = oldPlayer; + this.wasDeath = wasDeath; + } + } + + /** + * Fired when an Entity is started to be "tracked" by this player (the player receives updates about this entity, e.g. motion). + * + */ + public static class StartTracking extends PlayerEvent { + + /** + * The Entity now being tracked. + */ + public final Entity target; + + public StartTracking(EntityPlayer player, Entity target) + { + super(player); + this.target = target; + } + + } + + /** + * Fired when an Entity is stopped to be "tracked" by this player (the player no longer receives updates about this entity, e.g. motion). + * + */ + public static class StopTracking extends PlayerEvent { + + /** + * The Entity no longer being tracked. + */ + public final Entity target; + + public StopTracking(EntityPlayer player, Entity target) + { + super(player); + this.target = target; + } + + } } diff --git a/src/main/java/net/minecraftforge/event/entity/player/PlayerInteractEvent.java b/src/main/java/net/minecraftforge/event/entity/player/PlayerInteractEvent.java index b797bdb..06ccf2b 100644 --- a/src/main/java/net/minecraftforge/event/entity/player/PlayerInteractEvent.java +++ b/src/main/java/net/minecraftforge/event/entity/player/PlayerInteractEvent.java @@ -1,8 +1,10 @@ package net.minecraftforge.event.entity.player; -import cpw.mods.fml.common.eventhandler.Cancelable; +import static cpw.mods.fml.common.eventhandler.Event.Result.DEFAULT; +import static cpw.mods.fml.common.eventhandler.Event.Result.DENY; import net.minecraft.entity.player.EntityPlayer; -import static cpw.mods.fml.common.eventhandler.Event.Result.*; +import net.minecraft.world.World; +import cpw.mods.fml.common.eventhandler.Cancelable; @Cancelable public class PlayerInteractEvent extends PlayerEvent @@ -19,12 +21,19 @@ public final int y; public final int z; public final int face; + public final World world; public Result useBlock = DEFAULT; public Result useItem = DEFAULT; - + + @Deprecated public PlayerInteractEvent(EntityPlayer player, Action action, int x, int y, int z, int face) { + this(player, action, x, y, z, face, player.worldObj); + } + + public PlayerInteractEvent(EntityPlayer player, Action action, int x, int y, int z, int face, World world) + { super(player); this.action = action; this.x = x; @@ -32,6 +41,7 @@ this.z = z; this.face = face; if (face == -1) useBlock = DENY; + this.world = world; } @Override diff --git a/src/main/java/net/minecraftforge/event/world/NoteBlockEvent.java b/src/main/java/net/minecraftforge/event/world/NoteBlockEvent.java new file mode 100644 index 0000000..22c7ee8 --- /dev/null +++ b/src/main/java/net/minecraftforge/event/world/NoteBlockEvent.java @@ -0,0 +1,165 @@ +package net.minecraftforge.event.world; + +import com.google.common.base.Preconditions; + +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.world.World; +import net.minecraftforge.event.world.BlockEvent; +import cpw.mods.fml.common.eventhandler.Cancelable; +import cpw.mods.fml.common.eventhandler.Event; + +/** + * Base class for Noteblock Events + * + */ +public class NoteBlockEvent extends BlockEvent +{ + private int noteId; + + NoteBlockEvent(World world, int x, int y, int z, int meta, int note) + { + super(x, y, z, world, Blocks.noteblock, meta); + this.noteId = note; + } + + /** + * Get the Note the Noteblock is tuned to + * @return the Note + */ + public Note getNote() + { + return Note.fromId(noteId); + } + + /** + * Get the Octave of the note this Noteblock is tuned to + * @return the Octave + */ + public Octave getOctave() + { + return Octave.fromId(noteId); + } + + /** + * get the vanilla note-id, which contains information about both Note and Octave. Most modders should not need this. + * @return an ID for the note + */ + public int getVanillaNoteId() + { + return noteId; + } + + /** + * Set Note and Octave for this event.
    + * If octave is Octave.HIGH, note may only be Note.F_SHARP + * @param note the Note + * @param octave the Octave + */ + public void setNote(Note note, Octave octave) + { + Preconditions.checkArgument(octave != Octave.HIGH || note == Note.F_SHARP, "Octave.HIGH is only valid for Note.F_SHARP!"); + this.noteId = note.ordinal() + octave.ordinal() * 12; + } + + /** + * Fired when a Noteblock plays it's note. You can override the note and instrument + * Canceling this event will stop the note from playing. + */ + @Cancelable + public static class Play extends NoteBlockEvent + { + public Instrument instrument; + + public Play(World world, int x, int y, int z, int meta, int note, int instrument) + { + super(world, x, y, z, meta, note); + this.instrument = Instrument.fromId(instrument); + } + } + + /** + * Fired when a Noteblock is changed. You can adjust the note it will change to via {@link #setNote(Note, Octave)}. + * Canceling this event will not change the note and also stop the Noteblock from playing it's note. + */ + @Cancelable + public static class Change extends NoteBlockEvent + { + public final Note oldNote; + public final Octave oldOctave; + + public Change(World world, int x, int y, int z, int meta, int oldNote, int newNote) + { + super(world, x, y, z, meta, newNote); + this.oldNote = Note.fromId(oldNote); + this.oldOctave = Octave.fromId(oldNote); + } + } + + /** + * Describes the types of musical Instruments that can be played by a Noteblock. + * The Instrument being played can be overridden with {@link NoteBlockEvent.Play#setInstrument(Instrument)} + */ + public static enum Instrument + { + PIANO, + BASSDRUM, + SNARE, + CLICKS, + BASSGUITAR; + + // cache to avoid creating a new array every time + private static final Instrument[] values = values(); + + static Instrument fromId(int id) + { + return id < 0 || id > 4 ? PIANO : values[id]; + } + } + + /** + * Information about the pitch of a Noteblock note. + * For altered notes such as G-Sharp / A-Flat the Sharp variant is used here. + * + */ + public static enum Note + { + F_SHARP, + G, + G_SHARP, + A, + A_SHARP, + B, + C, + C_SHARP, + D, + D_SHARP, + E, + F; + + private static final Note[] values = values(); + + static Note fromId(int id) + { + return values[id % 12]; + } + } + + /** + * Describes the Octave of a Note being played by a Noteblock. + * Together with {@link Note} it fully describes the note. + * + */ + public static enum Octave + { + LOW, + MID, + HIGH; // only valid for F_SHARP + + static Octave fromId(int id) + { + return id < 12 ? LOW : id == 24 ? HIGH : MID; + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java b/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java index 3c5f261..199cf8b 100644 --- a/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java +++ b/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java @@ -349,8 +349,6 @@ @Override public boolean canDrain(World world, int x, int y, int z) { - return false; //isSourceBlock(world, x, y, z); + return isSourceBlock(world, x, y, z); } - @Override public Fluid getFluid(){ return null; } - @Override public float getFilledPercentage(World world, int x, int y, int z) { return 0; } } diff --git a/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java b/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java index 08afec9..302ded1 100644 --- a/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java +++ b/src/main/java/net/minecraftforge/fluids/BlockFluidFinite.java @@ -5,6 +5,7 @@ import net.minecraft.block.material.Material; import net.minecraft.init.Blocks; +import net.minecraft.util.MathHelper; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; @@ -310,14 +311,18 @@ @Override public FluidStack drain(World world, int x, int y, int z, boolean doDrain) { - return null; + if (doDrain) + { + world.setBlock(x, y, z, Blocks.air); + } + + return new FluidStack(getFluid(), + MathHelper.floor_float(getQuantaPercentage(world, x, y, z) * FluidContainerRegistry.BUCKET_VOLUME)); } @Override public boolean canDrain(World world, int x, int y, int z) { - return false; + return true; } - @Override public Fluid getFluid() { return null; } - @Override public float getFilledPercentage(World world, int x, int y, int z) { return 0; } } diff --git a/src/main/java/net/minecraftforge/fluids/FluidEvent.java b/src/main/java/net/minecraftforge/fluids/FluidEvent.java index 9164c68..a577690 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidEvent.java +++ b/src/main/java/net/minecraftforge/fluids/FluidEvent.java @@ -46,10 +46,23 @@ public static class FluidFillingEvent extends FluidEvent { public final IFluidTank tank; + public final int amount; + + /** + * @deprecated Will be removed in 1.8 + * + */ + @Deprecated public FluidFillingEvent(FluidStack fluid, World world, int x, int y, int z, IFluidTank tank) { + this(fluid, world, x, y, z, tank, -1); + } + + public FluidFillingEvent(FluidStack fluid, World world, int x, int y, int z, IFluidTank tank, int amount) + { super(fluid, world, x, y, z); this.tank = tank; + this.amount = amount; } } @@ -63,9 +76,22 @@ public static class FluidDrainingEvent extends FluidEvent { public final IFluidTank tank; + public final int amount; + + /** + * @deprecated Will be removed in 1.8 + * + */ + @Deprecated public FluidDrainingEvent(FluidStack fluid, World world, int x, int y, int z, IFluidTank tank) { + this(fluid, world, x, y, z, tank, -1); + } + + public FluidDrainingEvent(FluidStack fluid, World world, int x, int y, int z, IFluidTank tank, int amount) + { super(fluid, world, x, y, z); + this.amount = amount; this.tank = tank; } } diff --git a/src/main/java/net/minecraftforge/fluids/FluidTank.java b/src/main/java/net/minecraftforge/fluids/FluidTank.java index a992eb5..92333b4 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidTank.java +++ b/src/main/java/net/minecraftforge/fluids/FluidTank.java @@ -127,7 +127,7 @@ if (tile != null) { - FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(fluid, tile.getWorldObj(), tile.xCoord, tile.yCoord, tile.zCoord, this)); + FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(fluid, tile.getWorldObj(), tile.xCoord, tile.yCoord, tile.zCoord, this, fluid.amount)); } return fluid.amount; } @@ -150,7 +150,7 @@ if (tile != null) { - FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(fluid, tile.getWorldObj(), tile.xCoord, tile.yCoord, tile.zCoord, this)); + FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(fluid, tile.getWorldObj(), tile.xCoord, tile.yCoord, tile.zCoord, this, filled)); } return filled; } @@ -180,7 +180,7 @@ if (tile != null) { - FluidEvent.fireEvent(new FluidEvent.FluidDrainingEvent(fluid, tile.getWorldObj(), tile.xCoord, tile.yCoord, tile.zCoord, this)); + FluidEvent.fireEvent(new FluidEvent.FluidDrainingEvent(fluid, tile.getWorldObj(), tile.xCoord, tile.yCoord, tile.zCoord, this, drained)); } } return stack; diff --git a/src/main/java/net/minecraftforge/oredict/OreDictionary.java b/src/main/java/net/minecraftforge/oredict/OreDictionary.java index 2379318..32c4525 100644 --- a/src/main/java/net/minecraftforge/oredict/OreDictionary.java +++ b/src/main/java/net/minecraftforge/oredict/OreDictionary.java @@ -1,13 +1,18 @@ package net.minecraftforge.oredict; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Map; +import java.util.RandomAccess; import java.util.Map.Entry; +import java.util.Set; -import cpw.mods.fml.common.FMLLog; -import cpw.mods.fml.common.eventhandler.Event; import net.minecraft.block.Block; import net.minecraft.init.Blocks; import net.minecraft.init.Items; @@ -19,12 +24,21 @@ import net.minecraft.item.crafting.ShapelessRecipes; import net.minecraftforge.common.MinecraftForge; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.eventhandler.Event; + public class OreDictionary { private static boolean hasInit = false; - private static int maxID = 0; - private static HashMap oreIDs = new HashMap(); - private static HashMap> oreStacks = new HashMap>(); + private static List idToName = new ArrayList(); + private static Map nameToId = new HashMap(); + private static List> idToStack = Lists.newArrayList(); //ToDo: Unqualify to List when possible {1.8} + private static List> idToStackUn = Lists.newArrayList(); //ToDo: Unqualify to List when possible {1.8} + private static Map> stackToId = Maps.newHashMap(); + public static final ArrayList EMPTY_LIST = new UnmodifiableArrayList(Lists.newArrayList()); //ToDo: Unqualify to List when possible {1.8} /** * Minecraft changed from -1 to Short.MAX_VALUE in 1.5 release for the "block wildcard". Use this in case it @@ -63,16 +77,42 @@ registerOre("oreEmerald", Blocks.emerald_ore); registerOre("oreQuartz", Blocks.quartz_ore); registerOre("oreCoal", Blocks.coal_ore); + registerOre("blockGold", Blocks.gold_block); + registerOre("blockIron", Blocks.iron_block); + registerOre("blockLapis", Blocks.lapis_block); + registerOre("blockDiamond", Blocks.diamond_block); + registerOre("blockRedstone", Blocks.redstone_block); + registerOre("blockEmerald", Blocks.emerald_block); + registerOre("blockQuartz", Blocks.quartz_block); + registerOre("blockCoal", Blocks.coal_block); + registerOre("blockGlassColorless", Blocks.glass); + registerOre("blockGlass", Blocks.glass); + registerOre("blockGlass", new ItemStack(Blocks.stained_glass, 1, WILDCARD_VALUE)); + //blockGlass{Color} is added below with dyes + registerOre("paneGlassColorless", Blocks.glass_pane); + registerOre("paneGlass", Blocks.glass_pane); + registerOre("paneGlass", new ItemStack(Blocks.stained_glass_pane, 1, WILDCARD_VALUE)); + //paneGlass{Color} is added below with dyes + registerOre("ingotIron", Items.iron_ingot); + registerOre("ingotGold", Items.gold_ingot); + registerOre("ingotBrick", Items.brick); + registerOre("ingotBrickNether", Items.netherbrick); + registerOre("nuggetGold", Items.gold_nugget); registerOre("gemDiamond", Items.diamond); registerOre("gemEmerald", Items.emerald); + registerOre("gemQuartz", Items.quartz); registerOre("dustRedstone", Items.redstone); registerOre("dustGlowstone", Items.glowstone_dust); + registerOre("gemLapis", new ItemStack(Items.dye, 1, 4)); + registerOre("slimeball", Items.slime_ball); registerOre("glowstone", Blocks.glowstone); registerOre("cropWheat", Items.wheat); registerOre("cropPotato", Items.potato); registerOre("cropCarrot", Items.carrot); registerOre("stone", Blocks.stone); registerOre("cobblestone", Blocks.cobblestone); + registerOre("sandstone", new ItemStack(Blocks.sandstone, 1, WILDCARD_VALUE)); + registerOre("dye", new ItemStack(Items.dye, 1, WILDCARD_VALUE)); registerOre("record", Items.record_13); registerOre("record", Items.record_cat); registerOre("record", Items.record_blocks); @@ -96,41 +136,51 @@ replacements.put(new ItemStack(Blocks.stone, 1, WILDCARD_VALUE), "stone"); replacements.put(new ItemStack(Blocks.cobblestone), "cobblestone"); replacements.put(new ItemStack(Blocks.cobblestone, 1, WILDCARD_VALUE), "cobblestone"); + replacements.put(new ItemStack(Items.gold_ingot), "ingotGold"); + replacements.put(new ItemStack(Items.iron_ingot), "ingotIron"); replacements.put(new ItemStack(Items.diamond), "gemDiamond"); replacements.put(new ItemStack(Items.emerald), "gemEmerald"); replacements.put(new ItemStack(Items.redstone), "dustRedstone"); replacements.put(new ItemStack(Items.glowstone_dust), "dustGlowstone"); replacements.put(new ItemStack(Blocks.glowstone), "glowstone"); + replacements.put(new ItemStack(Items.slime_ball), "slimeball"); + replacements.put(new ItemStack(Blocks.glass), "blockGlassColorless"); // Register dyes String[] dyes = { - "dyeBlack", - "dyeRed", - "dyeGreen", - "dyeBrown", - "dyeBlue", - "dyePurple", - "dyeCyan", - "dyeLightGray", - "dyeGray", - "dyePink", - "dyeLime", - "dyeYellow", - "dyeLightBlue", - "dyeMagenta", - "dyeOrange", - "dyeWhite" + "Black", + "Red", + "Green", + "Brown", + "Blue", + "Purple", + "Cyan", + "LightGray", + "Gray", + "Pink", + "Lime", + "Yellow", + "LightBlue", + "Magenta", + "Orange", + "White" }; for(int i = 0; i < 16; i++) { ItemStack dye = new ItemStack(Items.dye, 1, i); + ItemStack block = new ItemStack(Blocks.stained_glass, 1, 15 - i); + ItemStack pane = new ItemStack(Blocks.stained_glass_pane, 1, 15 - i); if (!hasInit) { - registerOre(dyes[i], dye); + registerOre("dye" + dyes[i], dye); + registerOre("blockGlass" + dyes[i], block); + registerOre("paneGlass" + dyes[i], pane); } - replacements.put(dye, dyes[i]); + replacements.put(dye, "dye" + dyes[i]); + replacements.put(block, "blockGlass" + dyes[i]); + replacements.put(pane, "paneGlass" + dyes[i]); } hasInit = true; @@ -150,7 +200,9 @@ new ItemStack(Blocks.birch_stairs), new ItemStack(Blocks.jungle_stairs), new ItemStack(Blocks.acacia_stairs), - new ItemStack(Blocks.dark_oak_stairs) + new ItemStack(Blocks.dark_oak_stairs), + new ItemStack(Blocks.glass_pane), + new ItemStack(Blocks.stained_glass) }; List recipes = CraftingManager.getInstance().getRecipeList(); @@ -210,12 +262,14 @@ */ public static int getOreID(String name) { - Integer val = oreIDs.get(name); + Integer val = nameToId.get(name); if (val == null) { - val = maxID++; - oreIDs.put(name, val); - oreStacks.put(val, new ArrayList()); + idToName.add(name); + val = idToName.size() - 1; //0 indexed + nameToId.put(name, val); + idToStack.add(new ArrayList()); + idToStackUn.add(new UnmodifiableArrayList(idToStack.get(val))); } return val; } @@ -228,51 +282,67 @@ */ public static String getOreName(int id) { - for (Map.Entry entry : oreIDs.entrySet()) - { - if (id == entry.getValue()) - { - return entry.getKey(); - } - } - return "Unknown"; + return id < idToName.size() ? idToName.get(id) : "Unknown"; } /** * Gets the integer ID for the specified item stack. * If the item stack is not linked to any ore, this will return -1 and no new entry will be created. * - * @param itemStack The item stack of the ore. + * @param stack The item stack of the ore. * @return A number representing the ID for this ore type, or -1 if couldn't find it. */ - public static int getOreID(ItemStack itemStack) + @Deprecated // Use getOreIds below for more accuracy + public static int getOreID(ItemStack stack) { - if (itemStack == null) - { - return -1; - } + if (stack == null) return -1; - for(Entry> ore : oreStacks.entrySet()) + int id = Item.getIdFromItem(stack.getItem()); + List ids = stackToId.get(id); //Try the wildcard first + if (ids == null || ids.size() == 0) { - for(ItemStack target : ore.getValue()) - { - if(itemStack.getItem() == target.getItem() && (target.getItemDamage() == WILDCARD_VALUE || itemStack.getItemDamage() == target.getItemDamage())) - { - return ore.getKey(); - } - } + ids = stackToId.get(id | ((stack.getItemDamage() + 1) << 16)); // Mow the Meta specific one, +1 so that meta 0 is significant } - return -1; // didn't find it. + return (ids != null && ids.size() > 0) ? ids.get(0) : -1; + } + + /** + * Gets all the integer ID for the ores that the specified item stakc is registered to. + * If the item stack is not linked to any ore, this will return an empty array and no new entry will be created. + * + * @param stack The item stack of the ore. + * @return An array of ids that this ore is registerd as. + */ + public static int[] getOreIDs(ItemStack stack) + { + if (stack == null) return new int[0]; + + Set set = new HashSet(); + + int id = Item.getIdFromItem(stack.getItem()); + List ids = stackToId.get(id); + if (ids != null) set.addAll(ids); + ids = stackToId.get(id | ((stack.getItemDamage() + 1) << 16)); + if (ids != null) set.addAll(ids); + + Integer[] tmp = set.toArray(new Integer[set.size()]); + int[] ret = new int[tmp.length]; + for (int x = 0; x < tmp.length; x++) + ret[x] = tmp[x]; + return ret; } /** * Retrieves the ArrayList of items that are registered to this ore type. * Creates the list as empty if it did not exist. + * + * The returned List is unmodifiable, but will be updated if a new ore + * is registered using registerOre * * @param name The ore name, directly calls getOreID * @return An arrayList containing ItemStacks registered for this ore */ - public static ArrayList getOres(String name) + public static ArrayList getOres(String name) //TODO: 1.8 ArrayList -> List { return getOres(getOreID(name)); } @@ -284,25 +354,37 @@ */ public static String[] getOreNames() { - return oreIDs.keySet().toArray(new String[oreIDs.keySet().size()]); + return idToName.toArray(new String[idToName.size()]); } /** * Retrieves the ArrayList of items that are registered to this ore type. * Creates the list as empty if it did not exist. + * + * Warning: In 1.8, the return value will become a immutible list, + * and this function WILL NOT create the entry if the ID doesn't exist, + * IDs are intended to be internal OreDictionary things and modders + * should not ever code them in. * * @param id The ore ID, see getOreID - * @return An arrayList containing ItemStacks registered for this ore + * @return An List containing ItemStacks registered for this ore */ - public static ArrayList getOres(Integer id) + @Deprecated // Use the named version not int + public static ArrayList getOres(Integer id) //TODO: delete in 1.8 in favor of unboxed version below { - ArrayList val = oreStacks.get(id); - if (val == null) + return getOres((int)id.intValue()); + } + private static ArrayList getOres(int id) //TODO: change to ImmutibleList in 1.8, also make private + { + while (idToName.size() < id + 1) // TODO: Remove this in 1.8, this is only for backwards compatibility { - val = new ArrayList(); - oreStacks.put(id, val); + String name = "Filler: " + idToName.size(); + idToName.add(name); + nameToId.put(name, idToName.size() - 1); //0 indexed + idToStack.add(null); + idToStackUn.add(EMPTY_LIST); } - return val; + return idToStackUn.size() > id ? idToStackUn.get(id) : EMPTY_LIST; } private static boolean containsMatch(boolean strict, ItemStack[] inputs, ItemStack... targets) @@ -320,6 +402,21 @@ return false; } + private static boolean containsMatch(boolean strict, List inputs, ItemStack... targets) + { + for (ItemStack input : inputs) + { + for (ItemStack target : targets) + { + if (itemMatches(target, input, strict)) + { + return true; + } + } + } + return false; + } + public static boolean itemMatches(ItemStack target, ItemStack input, boolean strict) { if (input == null && target != null || input != null && target == null) @@ -332,10 +429,13 @@ //Convenience functions that make for cleaner code mod side. They all drill down to registerOre(String, int, ItemStack) public static void registerOre(String name, Item ore){ registerOre(name, new ItemStack(ore)); } public static void registerOre(String name, Block ore){ registerOre(name, new ItemStack(ore)); } - public static void registerOre(String name, ItemStack ore){ registerOre(name, getOreID(name), ore); } + public static void registerOre(String name, ItemStack ore){ registerOreImpl(name, ore); } + @Deprecated //Use named, not ID in 1.8+ public static void registerOre(int id, Item ore){ registerOre(id, new ItemStack(ore)); } + @Deprecated //Use named, not ID in 1.8+ public static void registerOre(int id, Block ore){ registerOre(id, new ItemStack(ore)); } - public static void registerOre(int id, ItemStack ore){ registerOre(getOreName(id), id, ore); } + @Deprecated //Use named, not ID in 1.8+ + public static void registerOre(int id, ItemStack ore){ registerOreImpl(getOreName(id), ore); } /** * Registers a ore item into the dictionary. @@ -345,11 +445,30 @@ * @param id The ID of the ore * @param ore The ore's ItemStack */ - private static void registerOre(String name, int id, ItemStack ore) + private static void registerOreImpl(String name, ItemStack ore) { - ArrayList ores = getOres(id); + if ("Unknown".equals(name)) return; //prevent bad IDs. + + int oreID = getOreID(name); + int hash = Item.getIdFromItem(ore.getItem()); + if (ore.getItemDamage() != WILDCARD_VALUE) + { + hash |= ((ore.getItemDamage() + 1) << 16); // +1 so 0 is significant + } + + //Add things to the baked version, and prevent duplicates + List ids = stackToId.get(hash); + if (ids != null && ids.contains(oreID)) return; + if (ids == null) + { + ids = Lists.newArrayList(); + stackToId.put(hash, ids); + } + ids.add(oreID); + + //Add to the unbaked version ore = ore.copy(); - ores.add(ore); + idToStack.get(oreID).add(ore); MinecraftForge.EVENT_BUS.post(new OreRegisterEvent(name, ore)); } @@ -364,4 +483,105 @@ this.Ore = ore; } } + + public static void rebakeMap() + { + //System.out.println("Baking OreDictionary:"); + stackToId.clear(); + for (int id = 0; id < idToStack.size(); id++) + { + List ores = idToStack.get(id); + if (ores == null) continue; + for (ItemStack ore : ores) + { + int hash = Item.getIdFromItem(ore.getItem()); + if (ore.getItemDamage() != WILDCARD_VALUE) + { + hash |= ((ore.getItemDamage() + 1) << 16); // +1 so meta 0 is significant + } + List ids = stackToId.get(hash); + if (ids == null) + { + ids = Lists.newArrayList(); + stackToId.put(hash, ids); + } + ids.add(id); + //System.out.println(id + " " + getOreName(id) + " " + Integer.toHexString(hash) + " " + ore); + } + } + } + + + //Pulled from Collections.UnmodifiableList, as we need to explicitly subclass ArrayList for backward compatibility. + //Delete this class in 1.8 when we loose the ArrayList specific return types. + private static class UnmodifiableArrayList extends ArrayList + { + final ArrayList list; + + UnmodifiableArrayList(ArrayList list) + { + super(0); + this.list = list; + } + + public ListIterator listIterator() {return listIterator(0); } + public boolean equals(Object o) { return o == this || list.equals(o); } + public int hashCode() { return list.hashCode(); } + public E get(int index) { return list.get(index); } + public int indexOf(Object o) { return list.indexOf(o); } + public int lastIndexOf(Object o) { return list.lastIndexOf(o); } + public int size() { return list.size(); } + public boolean isEmpty() { return list.isEmpty(); } + public boolean contains(Object o) { return list.contains(o); } + public Object[] toArray() { return list.toArray(); } + public T[] toArray(T[] a) { return list.toArray(a); } + public String toString() { return list.toString(); } + public boolean containsAll(Collection coll) { return list.containsAll(coll); } + + public E set(int index, E element) { throw new UnsupportedOperationException(); } + public void add(int index, E element) { throw new UnsupportedOperationException(); } + public E remove(int index) { throw new UnsupportedOperationException(); } + public boolean add(E e) { throw new UnsupportedOperationException(); } + public boolean remove(Object o) { throw new UnsupportedOperationException(); } + public void clear() { throw new UnsupportedOperationException(); } + public boolean removeAll(Collection coll) { throw new UnsupportedOperationException(); } + public boolean retainAll(Collection coll) { throw new UnsupportedOperationException(); } + public boolean addAll(Collection coll) { throw new UnsupportedOperationException(); } + public boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(); } + + public ListIterator listIterator(final int index) + { + return new ListIterator() + { + private final ListIterator i = list.listIterator(index); + public boolean hasNext() {return i.hasNext();} + public E next() {return i.next();} + public boolean hasPrevious() {return i.hasPrevious();} + public E previous() {return i.previous();} + public int nextIndex() {return i.nextIndex();} + public int previousIndex() {return i.previousIndex();} + + public void remove() { throw new UnsupportedOperationException(); } + public void set(E e) { throw new UnsupportedOperationException(); } + public void add(E e) { throw new UnsupportedOperationException(); } + }; + } + + public List subList(int fromIndex, int toIndex) + { + return Collections.unmodifiableList(list.subList(fromIndex, toIndex)); + } + + public Iterator iterator() + { + return new Iterator() + { + private final Iterator i = list.iterator(); + + public boolean hasNext() { return i.hasNext(); } + public E next() { return i.next(); } + public void remove() { throw new UnsupportedOperationException(); } + }; + } + } } diff --git a/src/main/java/net/minecraftforge/oredict/ShapedOreRecipe.java b/src/main/java/net/minecraftforge/oredict/ShapedOreRecipe.java index 108f6f0..dbd17c2 100644 --- a/src/main/java/net/minecraftforge/oredict/ShapedOreRecipe.java +++ b/src/main/java/net/minecraftforge/oredict/ShapedOreRecipe.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -209,7 +210,7 @@ if (target instanceof ItemStack) { - if (!checkItemEquals((ItemStack)target, slot)) + if (!OreDictionary.itemMatches((ItemStack)target, slot, false)) { return false; } @@ -218,9 +219,10 @@ { boolean matched = false; - for (ItemStack item : (ArrayList)target) + Iterator itr = ((ArrayList)target).iterator(); + while (itr.hasNext() && !matched) { - matched = matched || checkItemEquals(item, slot); + matched = OreDictionary.itemMatches(itr.next(), slot, false); } if (!matched) @@ -238,15 +240,6 @@ return true; } - private boolean checkItemEquals(ItemStack target, ItemStack input) - { - if (input == null && target != null || input != null && target == null) - { - return false; - } - return (target.getItem() == input.getItem() && (target.getItemDamage() == OreDictionary.WILDCARD_VALUE|| target.getItemDamage() == input.getItemDamage())); - } - public ShapedOreRecipe setMirrored(boolean mirror) { mirrored = mirror; diff --git a/src/main/java/net/minecraftforge/oredict/ShapelessOreRecipe.java b/src/main/java/net/minecraftforge/oredict/ShapelessOreRecipe.java index 82545cf..5b2599a 100644 --- a/src/main/java/net/minecraftforge/oredict/ShapelessOreRecipe.java +++ b/src/main/java/net/minecraftforge/oredict/ShapelessOreRecipe.java @@ -108,13 +108,14 @@ if (next instanceof ItemStack) { - match = checkItemEquals((ItemStack)next, slot); + match = OreDictionary.itemMatches((ItemStack)next, slot, false); } else if (next instanceof ArrayList) { - for (ItemStack item : (ArrayList)next) + Iterator itr = ((ArrayList)next).iterator(); + while (itr.hasNext() && !match) { - match = match || checkItemEquals(item, slot); + match = OreDictionary.itemMatches(itr.next(), slot, false); } } @@ -136,11 +137,6 @@ return required.isEmpty(); } - private boolean checkItemEquals(ItemStack target, ItemStack input) - { - return (target.getItem() == input.getItem() && (target.getItemDamage() == OreDictionary.WILDCARD_VALUE || target.getItemDamage() == input.getItemDamage())); - } - /** * Returns the input for this recipe, any mod accessing this value should never * manipulate the values in this array as it will effect the recipe itself. diff --git a/src/main/resources/assets/forge/lang/en_US.lang b/src/main/resources/assets/forge/lang/en_US.lang index 0110b4d..6a02f70 100644 --- a/src/main/resources/assets/forge/lang/en_US.lang +++ b/src/main/resources/assets/forge/lang/en_US.lang @@ -3,4 +3,8 @@ commands.forge.tps.summary=%s : Mean tick time: %d ms. Mean TPS: %d commands.forge.tracking.te.enabled=Tile Entity tracking enabled for %d seconds. -forge.texture.preload.warning=Warning: Texture %s not preloaded, will cause render glitches! \ No newline at end of file +forge.texture.preload.warning=Warning: Texture %s not preloaded, will cause render glitches! +forge.client.shutdown.internal=Shutting down internal server... +forge.update.newversion=New Forge version available: %s +forge.update.beta.1=%sWARNING: %sForge Beta +forge.update.beta.2=Major issues may arise, verify before reporting. \ No newline at end of file diff --git a/src/main/resources/fmlversion.properties b/src/main/resources/fmlversion.properties index 1ea4e66..5684d41 100644 --- a/src/main/resources/fmlversion.properties +++ b/src/main/resources/fmlversion.properties @@ -1,6 +1,6 @@ fmlbuild.major.number=7 fmlbuild.minor.number=2 -fmlbuild.revision.number=190 -fmlbuild.build.number=35 +fmlbuild.revision.number=211 +fmlbuild.build.number=22 fmlbuild.mcversion=1.7.2 fmlbuild.mcpversion=9.03 diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index 8a3c3af..5db7d2a 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -10,3 +10,8 @@ net/minecraft/world/World.drawCloudsBody(F)Lnet/minecraft/util/Vec3;=|p_72824_1_ net/minecraft/world/World.canBlockFreezeBody(IIIZ)Z=|p_72834_1_,p_72834_2_,p_72834_3_,p_72834_4_ net/minecraft/world/biome/BiomeGenBase.(IZ)V=|p_i1971_1_,register +net/minecraft/world/chunk/storage/AnvilChunkLoader.loadChunk__Async(Lnet/minecraft/world/World;II)[Ljava/lang/Object;=|p_75815_1_,p_75815_2_,p_75815_3_ +net/minecraft/world/chunk/storage/AnvilChunkLoader.checkedReadChunkFromNBT__Async(Lnet/minecraft/world/World;IILnet/minecraft/nbt/NBTTagCompound;)[Ljava/lang/Object;=|p_75822_1_,p_75822_2_,p_75822_3_,p_75822_4_ +net/minecraft/world/chunk/storage/AnvilChunkLoader.loadEntities(Lnet/minecraft/world/World;Lnet/minecraft/nbt/NBTTagCompound;Lnet/minecraft/world/chunk/Chunk;)V=|p_75823_1_,p_75823_2_,chunk +net/minecraft/world/gen/ChunkProviderServer.loadChunk(II;Ljava/lang/Runnable;)Lnet/minecraft/world/chunk/Chunk;=|p_73158_1,p_73158_2,runnable +net/minecraft/world/gen/ChunkProviderServer.originalLoadChunk(II)Lnet/minecraft/world/chunk/Chunk;=|p_73158_1_,p_73158_2_ diff --git a/src/main/resources/forge_at.cfg b/src/main/resources/forge_at.cfg index 984c702..794c113 100644 --- a/src/main/resources/forge_at.cfg +++ b/src/main/resources/forge_at.cfg @@ -178,4 +178,8 @@ public net.minecraft.client.renderer.entity.RenderPlayer field_77109_a #modelBipedMain public net.minecraft.client.renderer.entity.RenderPlayer field_77108_b #modelArmorChestplate public net.minecraft.client.renderer.entity.RenderPlayer field_77111_i #modelArmor - +# ChunkProviderServer +public net.minecraft.world.gen.ChunkProviderServer field_73246_d # currentChunkProvider +public net.minecraft.world.gen.ChunkProviderServer field_73244_f # loadedChunkHashMap +public net.minecraft.world.gen.ChunkProviderServer field_73245_g # loadedChunks +public net.minecraft.world.gen.ChunkProviderServer field_73251_h # worldObj diff --git a/src/main/resources/lgpl-3.0.txt b/src/main/resources/lgpl-3.0.txt new file mode 100644 index 0000000..02bbb60 --- /dev/null +++ b/src/main/resources/lgpl-3.0.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file