diff --git a/src/main/java/com/falsepattern/endlessids/EndlessIDs.java b/src/main/java/com/falsepattern/endlessids/EndlessIDs.java index b4d3359..08d1504 100644 --- a/src/main/java/com/falsepattern/endlessids/EndlessIDs.java +++ b/src/main/java/com/falsepattern/endlessids/EndlessIDs.java @@ -38,9 +38,32 @@ public class EndlessIDs { @Mod.EventHandler public void preInit(FMLPreInitializationEvent e) { + LOG.info("Current configuration:"); + logConfig(" Biome Extension", GeneralConfig.extendBiome); + logConfig(" Block/Item Extension", GeneralConfig.extendBlockItem); + if (GeneralConfig.extendBlockItem) { + logConfig(" Extra Item Bits", GeneralConfig.extraItemIDBits); + logConfig(" Extra Block Bits", GeneralConfig.extraBlockIDBits); + } + logConfig("DataWatcher Extension", GeneralConfig.extendDataWatcher); + logConfig("Enchantment Extension", GeneralConfig.extendEnchantment); + logConfig(" Potion Extension", GeneralConfig.extendPotion); + logConfig(" Redstone Extension", GeneralConfig.extendRedstone); + if (GeneralConfig.extendRedstone) { + logConfig(" Max redstone level", GeneralConfig.maxRedstone); + LOG.warn("(Redstone extension is an experimental and dangerous feature, and should NEVER be used in worlds that you worry about getting corrupted!)"); + } patchManager.preInit(); } + private static void logConfig(String name, boolean value) { + LOG.info(name + (value ? ": Enabled" : ": Disabled")); + } + + private static void logConfig(String name, int value) { + LOG.info(name + ": " + value); + } + @Mod.EventHandler public void init(FMLInitializationEvent e) { patchManager.init(); diff --git a/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java b/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java index 488d4fe..a0981e7 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java +++ b/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java @@ -1,5 +1,7 @@ package com.falsepattern.endlessids.asm; +import com.falsepattern.endlessids.asm.stubpackage.cpw.mods.fml.common.registry.GameData; +import com.falsepattern.endlessids.util.WeakIdentityHashMap; import lombok.var; import net.minecraft.block.Block; @@ -11,14 +13,19 @@ import java.util.Collections; import java.util.WeakHashMap; public class GameDataHooks { - private static final WeakHashMap> itemBlockCache = new WeakHashMap<>(); - - public static void cacheItemBlock(ItemBlock itemBlock) { - itemBlockCache.put(itemBlock.field_150939_a, new WeakReference<>(itemBlock)); + private static WeakIdentityHashMap> forGameData(GameData gameData) { + if (gameData.endlessIDsItemBlockCache == null) { + gameData.endlessIDsItemBlockCache = new WeakIdentityHashMap<>(); + } + return gameData.endlessIDsItemBlockCache; } - public static ItemBlock fetchCachedItemBlock(Block block) { - var weakItem = itemBlockCache.get(block); + public static void cacheItemBlock(ItemBlock itemBlock, GameData gameData) { + forGameData(gameData).put(itemBlock.field_150939_a, new WeakReference<>(itemBlock)); + } + + public static ItemBlock fetchCachedItemBlock(Block block, GameData gameData) { + var weakItem = forGameData(gameData).get(block); if (weakItem != null) { return weakItem.get(); } diff --git a/src/main/java/com/falsepattern/endlessids/asm/stubpackage/cpw/mods/fml/common/registry/GameData.java b/src/main/java/com/falsepattern/endlessids/asm/stubpackage/cpw/mods/fml/common/registry/GameData.java new file mode 100644 index 0000000..ff4b8d5 --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/asm/stubpackage/cpw/mods/fml/common/registry/GameData.java @@ -0,0 +1,12 @@ +package com.falsepattern.endlessids.asm.stubpackage.cpw.mods.fml.common.registry; + +import com.falsepattern.endlessids.util.WeakIdentityHashMap; + +import net.minecraft.block.Block; +import net.minecraft.item.ItemBlock; + +import java.lang.ref.WeakReference; + +public class GameData { + public WeakIdentityHashMap> endlessIDsItemBlockCache; +} diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java index 821c95b..65dc43d 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java @@ -7,6 +7,7 @@ import lombok.var; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; @@ -17,6 +18,8 @@ public class GameDataAccelerator implements IClassNodeTransformer { private static final int MASK_REGISTER_ITEM = 0b10; @Override public void transform(ClassNode classNode, boolean obfuscated) { + var cacheField = new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "endlessIDsItemBlockCache", "Lcom/falsepattern/endlessids/util/WeakIdentityHashMap;", null, null); + classNode.fields.add(cacheField); int tweaksLeft = MASK_REGISTER_BLOCK | MASK_REGISTER_ITEM; for (val method: classNode.methods) { if ((tweaksLeft & MASK_REGISTER_BLOCK) != 0 && @@ -53,7 +56,8 @@ public class GameDataAccelerator implements IClassNodeTransformer { continue; iter.previous(); iter.add(new InsnNode(Opcodes.DUP)); - iter.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/falsepattern/endlessids/asm/GameDataHooks", "cacheItemBlock", "(Lnet/minecraft/item/ItemBlock;)V", false)); + iter.add(new VarInsnNode(Opcodes.ALOAD, 0)); + iter.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/falsepattern/endlessids/asm/GameDataHooks", "cacheItemBlock", "(Lnet/minecraft/item/ItemBlock;Lcpw/mods/fml/common/registry/GameData;)V", false)); return; } transformError(method); @@ -82,7 +86,8 @@ public class GameDataAccelerator implements IClassNodeTransformer { iter.next(); iter.remove(); iter.add(new VarInsnNode(Opcodes.ALOAD, 1)); - iter.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/falsepattern/endlessids/asm/GameDataHooks", "fetchCachedItemBlock", "(Lnet/minecraft/block/Block;)Lnet/minecraft/item/ItemBlock;", false)); + iter.add(new VarInsnNode(Opcodes.ALOAD, 0)); + iter.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/falsepattern/endlessids/asm/GameDataHooks", "fetchCachedItemBlock", "(Lnet/minecraft/block/Block;Lcpw/mods/fml/common/registry/GameData;)Lnet/minecraft/item/ItemBlock;", false)); iter.add(new VarInsnNode(Opcodes.ASTORE, 4)); iter.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/falsepattern/endlessids/asm/GameDataHooks", "fakeIterable", "()Ljava/lang/Iterable;", false)); return; diff --git a/src/main/java/com/falsepattern/endlessids/config/GeneralConfig.java b/src/main/java/com/falsepattern/endlessids/config/GeneralConfig.java index 8c791f1..ecc0689 100644 --- a/src/main/java/com/falsepattern/endlessids/config/GeneralConfig.java +++ b/src/main/java/com/falsepattern/endlessids/config/GeneralConfig.java @@ -56,10 +56,30 @@ public class GeneralConfig { "The default setting sets it to 4k (vanilla).\n" + "increase if necessary (when running out of BLOCK IDs in your modpack)\n" + "Only effective if extendBlockItem is enabled.\n" + - "(0: 16M, 1: 8M, 2: 4M, 3: 2M, 4: 1M, 5: 512K, 6: 256K, 7: 128K, 8: 64K, 9: 32K, 10: 16K, 11: 8K, 12: 4K)\n") + "Effective values: (0: 4k, 1: 8k, 2: 16k, 3: 32k, 4: 64k, 5: 128k, 6: 256k, 7: 512k, 8: 1M, 9: 2M, 10: 4M, 11: 8M, 12: 16M)\n" + + "The effective value of this variable must always be smaller or equal to the effective value of extraItemIDBits") @Config.RangeInt(min = 0, max = 12) - @Config.DefaultInt(12) + @Config.DefaultInt(0) + public static int extraBlockIDBits; + @Config.Comment("Use this to tune the amount of available item IDs.\n" + + "The vanilla default is 32k item IDs, while the maximum is 16 million.\n" + + "The default setting sets it to 32k (vanilla).\n" + + "increase if necessary (when running out of ITEM IDs in your modpack)\n" + + "Only effective if extendBlockItem is enabled.\n" + + "Effective values: (0: 32k, 1: 64k, 2: 128k, 3: 256k, 4: 512k, 5: 1M, 6: 2M, 7: 4M, 8: 8M, 9: 16M)\n" + + "The effective value of this variable must always be equal or larger than the effective value of extraBlockIDBits") + @Config.RangeInt(min = 0, + max = 9) + @Config.DefaultInt(0) + public static int extraItemIDBits; + + + @Config.Comment("DEPRECATED, will be removed in the next version of EndlessIDs!\n" + + "This property does nothing.\n" + + "Use extraBlockIDBits and extraItemIDBits instead.") + @Config.DefaultInt(0) + @Deprecated public static int countCorrectionBits; static { diff --git a/src/main/java/com/falsepattern/endlessids/constants/ExtendedConstants.java b/src/main/java/com/falsepattern/endlessids/constants/ExtendedConstants.java index d78a57f..01019c8 100644 --- a/src/main/java/com/falsepattern/endlessids/constants/ExtendedConstants.java +++ b/src/main/java/com/falsepattern/endlessids/constants/ExtendedConstants.java @@ -7,7 +7,8 @@ public class ExtendedConstants { //Tunables public static final int bitsPerID = 24; public static final int bitsPerMetadata = 16; - public static final int countCorrectionBits = GeneralConfig.countCorrectionBits; + public static final int countCorrectionItemBits = 9 - GeneralConfig.extraItemIDBits; + public static final int countCorrectionBlockBits = 12 - GeneralConfig.extraBlockIDBits; public static final int watchableBits = 7; public static final int bitsPerBiome = 16; @@ -22,10 +23,10 @@ public class ExtendedConstants { public static final int bytesPerIDPlusMetadata = (nibblesPerID + nibblesPerMetadata + 1) / 2; public static final int blockIDMask = (1 << bitsPerID) - 1; - public static final int blockIDCount = 1 << (bitsPerID - countCorrectionBits); + public static final int blockIDCount = 1 << (bitsPerID - countCorrectionBlockBits); public static final int maxBlockID = blockIDCount - 1; - public static final int itemIDCount = 1 << (bitsPerID); + public static final int itemIDCount = 1 << (bitsPerID - countCorrectionItemBits); public static final int maxItemID = itemIDCount - 1; public static final int bitsPerBlock = 8 + bitsPerID + bitsPerMetadata; diff --git a/src/main/java/com/falsepattern/endlessids/util/WeakIdentityHashMap.java b/src/main/java/com/falsepattern/endlessids/util/WeakIdentityHashMap.java new file mode 100644 index 0000000..7c4f7be --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/util/WeakIdentityHashMap.java @@ -0,0 +1,71 @@ +package com.falsepattern.endlessids.util; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Like WeakHashMap, but uses identity instead of equality when comparing keys. + */ +public class WeakIdentityHashMap { + private final HashMap,V> mMap = new HashMap<>(); + private final ReferenceQueue mRefQueue = new ReferenceQueue<>(); + private void cleanUp() { + Reference ref; + while ((ref = mRefQueue.poll()) != null) { + mMap.remove(ref); + } + } + public void put(K key, V value) { + cleanUp(); + mMap.put(new CmpWeakReference<>(key, mRefQueue), value); + } + public V get(K key) { + cleanUp(); + return mMap.get(new CmpWeakReference<>(key)); + } + public Collection values() { + cleanUp(); + return mMap.values(); + } + public Set, V>> entrySet() { + return mMap.entrySet(); + } + public int size() { + cleanUp(); + return mMap.size(); + } + public boolean isEmpty() { + cleanUp(); + return mMap.isEmpty(); + } + private static class CmpWeakReference extends WeakReference { + private final int mHashCode; + public CmpWeakReference(K key) { + super(key); + mHashCode = System.identityHashCode(key); + } + public CmpWeakReference(K key, ReferenceQueue refQueue) { + super(key, refQueue); + mHashCode = System.identityHashCode(key); + } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + K k = get(); + if (k != null && o instanceof CmpWeakReference) { + return ((CmpWeakReference) o).get() == k; + } + return false; + } + @Override + public int hashCode() { + return mHashCode; + } + } +} \ No newline at end of file