diff --git a/.gitignore b/.gitignore index 412529c..a5ada46 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,5 @@ gradle-app.setting # End of https://www.toptal.com/developers/gitignore/api/intellij+all,gradle,forgegradle,java !lib/*.jar + +srgmap.cfg \ No newline at end of file diff --git a/README.MD b/README.MD index 11a081a..5a02004 100644 --- a/README.MD +++ b/README.MD @@ -6,6 +6,7 @@ Comparison of the known limits: | | Vanilla | NotEnoughIDs | EndlessIDs | |-----------------|----------------------------------------------------|--------------|----------------------------------| | Block IDs | 4,096 | 32,000 | 16,777,216 since 1.3 | +| Block Metadata | 16 | 16 | 65,536 since 1.5 | | Item IDs | 32,000 | 32,000 | 16,777,216 since 1.3 | | Biome IDs | 256 | 256 | 65,536 since 1.4 | | Entity IDs | 256 | 256 | 256 right now, extension planned | diff --git a/src/main/java/com/falsepattern/endlessids/Hooks.java b/src/main/java/com/falsepattern/endlessids/Hooks.java index cd9d004..78f0687 100644 --- a/src/main/java/com/falsepattern/endlessids/Hooks.java +++ b/src/main/java/com/falsepattern/endlessids/Hooks.java @@ -11,29 +11,33 @@ import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.chunk.Chunk; -import java.nio.ByteBuffer; -import java.nio.ShortBuffer; - public class Hooks { - public static byte[] getBlockData(final IExtendedBlockStorageMixin ebs) { + public static void getBlockData(final IExtendedBlockStorageMixin ebs, final byte[] data, final int offset) { val lsb = ebs.getLSB(); val msb = ebs.getMSB(); - final byte[] ret = new byte[lsb.length * 3]; - ByteBuffer.wrap(ret).asShortBuffer().put(lsb); - System.arraycopy(msb, 0, ret, lsb.length * 2, lsb.length); - return ret; + Unsafer.arraycopy(lsb, 0, data, offset, lsb.length); + System.arraycopy(msb, 0, data, offset + lsb.length * 2, msb.length); } public static void setBlockData(final IExtendedBlockStorageMixin ebs, final byte[] data, final int offset) { val lsb = ebs.getLSB(); val msb = ebs.getMSB(); - val lsbBytes = lsb.length * 2; - ShortBuffer.wrap(ebs.getLSB()).put(ByteBuffer.wrap(data, offset, lsbBytes).asShortBuffer()); - System.arraycopy(data, offset + lsbBytes, msb, 0, msb.length); + Unsafer.arraycopy(data, offset, lsb, 0, lsb.length); + System.arraycopy(data, offset + lsb.length * 2, msb, 0, msb.length); } - public static void writeChunkToNbt(final NBTTagCompound nbt, final IExtendedBlockStorageMixin ebs) { + public static void getBlockMeta(final IExtendedBlockStorageMixin ebs, final byte[] data, final int offset) { + val meta = ebs.getMetaArray(); + Unsafer.arraycopy(meta, 0, data, offset, meta.length); + } + + public static void setBlockMeta(final IExtendedBlockStorageMixin ebs, final byte[] data, final int offset) { + val meta = ebs.getMetaArray(); + Unsafer.arraycopy(data, offset, meta, 0, meta.length); + } + + public static void writeBlockDataToNBT(final NBTTagCompound nbt, final IExtendedBlockStorageMixin ebs) { val ebsLSB = ebs.getLSB(); val ebsMSB = ebs.getMSB(); @@ -82,7 +86,7 @@ public class Hooks { } } - public static void readChunkFromNbt(final IExtendedBlockStorageMixin ebs, final NBTTagCompound nbt) { + public static void readBlockDataFromNBT(final IExtendedBlockStorageMixin ebs, final NBTTagCompound nbt) { assert nbt.hasKey("Blocks"); val byte1Data = nbt.getByteArray("Blocks"); val byte2BottomNibbleData = nbt.hasKey("Add") ? nbt.getByteArray("Add") : null; @@ -183,8 +187,8 @@ public class Hooks { } } - public static void oldBiomeByteToNewShortArray(byte[] byteArray, short[] shortArray) { - for (int i = 0; i < 256; i++) { + private static void scatter(byte[] byteArray, short[] shortArray) { + for (int i = 0; i < byteArray.length; i++) { shortArray[i] = (short) (byteArray[i] & 0xff); } } @@ -218,7 +222,7 @@ public class Hooks { } else if (nbt.hasKey("Biomes16", 7)) { byteToShortArrayLegacy(nbt.getByteArray("Biomes16"), data); } else if (nbt.hasKey("Biomes", 7)) { - oldBiomeByteToNewShortArray(nbt.getByteArray("Biomes"), data); + scatter(nbt.getByteArray("Biomes"), data); } else { put = false; } @@ -226,4 +230,17 @@ public class Hooks { ((IChunkMixin) chunk).setBiomeShortArray(data); } } + + public static void readBlockMetaFromNBT(IExtendedBlockStorageMixin instance, NBTTagCompound nbt) { + short[] data = instance.getMetaArray(); + if (nbt.hasKey("Data16", 7)) { + byteToShortArray(nbt.getByteArray("Data16"), 0, data, 0, data.length * 2); + } else { + scatter(nbt.getByteArray("Data"), data); + } + } + + public static void writeBlockMetaToNBT(IExtendedBlockStorageMixin instance, NBTTagCompound nbt) { + nbt.setByteArray("Data16", shortToByteArray(instance.getMetaArray())); + } } diff --git a/src/main/java/com/falsepattern/endlessids/Unsafer.java b/src/main/java/com/falsepattern/endlessids/Unsafer.java new file mode 100644 index 0000000..2e70190 --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/Unsafer.java @@ -0,0 +1,59 @@ +package com.falsepattern.endlessids; + +import lombok.val; +import sun.misc.Unsafe; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + +public class Unsafer { + public static final Unsafe UNSAFE; + static { + Field unsafeField = null; + Unsafe unsafe = null; + for (val field: Unsafe.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && field.getType().equals(Unsafe.class)) { + unsafeField = field; + break; + } + } + if (unsafeField != null) { + unsafeField.setAccessible(true); + try { + unsafe = (Unsafe) unsafeField.get(null); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + if (unsafe == null) { + UNSAFE = null; + new IllegalStateException("Could not retrieve Unsafe! Falling back to bytebuffers!").printStackTrace(); + } else { + UNSAFE = unsafe; + } + } + + public static void arraycopy(short[] source, int sourceOffset, byte[] target, int targetOffset, int shorts) { + if (UNSAFE != null) { + val sourceOffsetBytes = sourceOffset * 2; + val arrayOffsetSource = UNSAFE.arrayBaseOffset(source.getClass()); + val arrayOffsetTarget = UNSAFE.arrayBaseOffset(target.getClass()); + UNSAFE.copyMemory(source, arrayOffsetSource + sourceOffsetBytes, target, arrayOffsetTarget + targetOffset, (long)shorts << 1); + } else { + ByteBuffer.wrap(target, targetOffset, target.length - targetOffset).asShortBuffer().put(source, sourceOffset, shorts); + } + } + + public static void arraycopy(byte[] source, int sourceOffset, short[] target, int targetOffset, int shorts) { + if (UNSAFE != null) { + val targetOffsetBytes = targetOffset * 2; + val arrayOffsetSource = UNSAFE.arrayBaseOffset(source.getClass()); + val arrayOffsetTarget = UNSAFE.arrayBaseOffset(target.getClass()); + UNSAFE.copyMemory(source, arrayOffsetSource + sourceOffset, target, arrayOffsetTarget + targetOffsetBytes, (long)shorts << 1); + } else { + ShortBuffer.wrap(target, targetOffset, target.length - targetOffset).put(ByteBuffer.wrap(source, sourceOffset, shorts * 2).asShortBuffer()); + } + } +} diff --git a/src/main/java/com/falsepattern/endlessids/config/GeneralConfig.java b/src/main/java/com/falsepattern/endlessids/config/GeneralConfig.java index fbbc9ac..622ed3f 100644 --- a/src/main/java/com/falsepattern/endlessids/config/GeneralConfig.java +++ b/src/main/java/com/falsepattern/endlessids/config/GeneralConfig.java @@ -47,6 +47,20 @@ public class GeneralConfig { @Config.DefaultBoolean(true) public static boolean extendPotion; + @Config.Comment("Extends the maximum redstone signal strength.\n" + + "Only has effect with extendBlockItem enabled.\n" + + "WARNING: THIS IS HERE ONLY FOR FUN, IT WILL DEFINITELY CORRUPT YOUR WORLD!") + @Config.DefaultBoolean(false) + public static boolean extendRedstone; + + @Config.Comment("Increases the max signal strength of redstone.\n" + + "Only has effect with both extendBlockItem and extendRedstone enabled.\n" + + "Vanilla value is 15.") + @Config.RangeInt(min = 15, + max = 127) + @Config.DefaultInt(15) + public static int maxRedstone; + @Config.Comment( "Use this to tune the amount of available block IDs.\n" + "Minecraft contains some internal code that uses a HUGE amount of RAM with too many block IDs available.\n" + diff --git a/src/main/java/com/falsepattern/endlessids/constants/ExtendedConstants.java b/src/main/java/com/falsepattern/endlessids/constants/ExtendedConstants.java index ea241dd..60456df 100644 --- a/src/main/java/com/falsepattern/endlessids/constants/ExtendedConstants.java +++ b/src/main/java/com/falsepattern/endlessids/constants/ExtendedConstants.java @@ -5,7 +5,7 @@ import com.falsepattern.endlessids.config.GeneralConfig; public class ExtendedConstants { //Tunables public static final int bitsPerID = 24; - public static final int bitsPerMetadata = 4; + public static final int bitsPerMetadata = 16; public static final int countCorrectionBits = GeneralConfig.countCorrectionBits; public static final int watchableBits = 7; diff --git a/src/main/java/com/falsepattern/endlessids/mixin/helpers/IExtendedBlockStorageMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/helpers/IExtendedBlockStorageMixin.java index c15a7d0..871327a 100644 --- a/src/main/java/com/falsepattern/endlessids/mixin/helpers/IExtendedBlockStorageMixin.java +++ b/src/main/java/com/falsepattern/endlessids/mixin/helpers/IExtendedBlockStorageMixin.java @@ -8,4 +8,6 @@ public interface IExtendedBlockStorageMixin { short[] getLSB(); byte[] getMSB(); + + short[] getMetaArray(); } diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/blockitem/vanilla/ChunkMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/blockitem/vanilla/ChunkMixin.java index b37cfe1..9f2295e 100644 --- a/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/blockitem/vanilla/ChunkMixin.java +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/blockitem/vanilla/ChunkMixin.java @@ -10,9 +10,12 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraft.network.play.server.S21PacketChunkData; import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.NibbleArray; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import java.util.Iterator; @@ -63,4 +66,50 @@ public abstract class ChunkMixin { target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getBlockLSBArray()[B")), require = 1) private void noCopy(Object src, int srcPos, Object dest, int destPos, int length) {} + + + + @Inject(method = "fillChunk", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;"), + locals = LocalCapture.CAPTURE_FAILHARD, + require = 1) + private void hookSetBlockMeta(byte[] p_76607_1_, int p_76607_2_, int p_76607_3_, boolean p_76607_4_, CallbackInfo ci, Iterator iterator, int k, boolean flag1, int l) { + Hooks.setBlockMeta((IExtendedBlockStorageMixin) this.storageArrays[l], p_76607_1_, k); + } + + private static NibbleArray fakeNarray; + + @Redirect(method = "fillChunk", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;"), + require = 1) + private NibbleArray hookSetBlockMetaNoArray(ExtendedBlockStorage instance) { + return fakeNarray != null ? fakeNarray : (fakeNarray = new NibbleArray(null, 0)); + } + + @Redirect(method = "fillChunk", + at = @At(value = "FIELD", + target = "Lnet/minecraft/world/chunk/NibbleArray;data:[B", + args = "array=length"), + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;"), + to = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getBlocklightArray()Lnet/minecraft/world/chunk/NibbleArray;")), + expect = 2, + require = 2) + private int hookSetBlockMetaFakeBytesLength(byte[] array) { + return 16 * 16 * 16 * (ExtendedConstants.nibblesPerMetadata >>> 1); + } + + @Redirect(method = "fillChunk", + at = @At(value = "INVOKE", + target = "Ljava/lang/System;arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V", + ordinal = 0), + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;")), + require = 1) + private void hookSetBlockMetaNoCopy(Object src, int srcPos, Object dest, int destPos, int length) { + + } } diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/blockitem/vanilla/NetHandlerPlayClientMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/blockitem/vanilla/NetHandlerPlayClientMixin.java index e139ac2..d8943db 100644 --- a/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/blockitem/vanilla/NetHandlerPlayClientMixin.java +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/blockitem/vanilla/NetHandlerPlayClientMixin.java @@ -38,7 +38,7 @@ public abstract class NetHandlerPlayClientMixin { short idLSB = dataInput.readShort(); byte idMSB = dataInput.readByte(); int id = (idLSB & 0xFFFF) | ((idMSB & 0xFF) << 16); - int meta = dataInput.readByte() & 15; + int meta = dataInput.readShort() & 0xFFFF; int x = pos >> 12 & 15; int z = pos >> 8 & 15; int y = pos & 255; diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/redstone/BlockRedstoneWireMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/redstone/BlockRedstoneWireMixin.java new file mode 100644 index 0000000..530a338 --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/redstone/BlockRedstoneWireMixin.java @@ -0,0 +1,18 @@ +package com.falsepattern.endlessids.mixin.mixins.client.redstone; + +import com.falsepattern.endlessids.config.GeneralConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +import net.minecraft.block.BlockRedstoneWire; + +@Mixin(BlockRedstoneWire.class) +public abstract class BlockRedstoneWireMixin { + @ModifyConstant(method = "randomDisplayTick", + constant = @Constant(floatValue = 15.0F), + require = 1) + private float bigRedstone(float constant) { + return GeneralConfig.maxRedstone; + } +} diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/redstone/RenderBlocksMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/redstone/RenderBlocksMixin.java new file mode 100644 index 0000000..fdff7db --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/client/redstone/RenderBlocksMixin.java @@ -0,0 +1,18 @@ +package com.falsepattern.endlessids.mixin.mixins.client.redstone; + +import com.falsepattern.endlessids.config.GeneralConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +import net.minecraft.client.renderer.RenderBlocks; + +@Mixin(RenderBlocks.class) +public abstract class RenderBlocksMixin { + @ModifyConstant(method = "renderBlockRedstoneWire", + constant = @Constant(floatValue = 15.0F), + require = 1) + private float bigRedstone(float constant) { + return GeneralConfig.maxRedstone; + } +} diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/AnvilChunkLoaderMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/AnvilChunkLoaderMixin.java index 2e89afc..5149692 100644 --- a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/AnvilChunkLoaderMixin.java +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/AnvilChunkLoaderMixin.java @@ -3,15 +3,11 @@ package com.falsepattern.endlessids.mixin.mixins.common.blockitem.vanilla; import com.falsepattern.endlessids.Hooks; import com.falsepattern.endlessids.mixin.helpers.IExtendedBlockStorageMixin; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.world.World; -import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.NibbleArray; import net.minecraft.world.chunk.storage.AnvilChunkLoader; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; @@ -41,8 +37,8 @@ public abstract class AnvilChunkLoaderMixin { to = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getBlockMSBArray()Lnet/minecraft/world/chunk/NibbleArray;")), require = 1) - private void redirectBlockDataWrite(NBTTagCompound instance, String p_74773_1_, byte[] p_74773_2_) { - Hooks.writeChunkToNbt(instance, ebs); + private void redirectBlockDataWrite(NBTTagCompound instance, String key, byte[] value) { + Hooks.writeBlockDataToNBT(instance, ebs); ebs = null; } @@ -54,6 +50,38 @@ public abstract class AnvilChunkLoaderMixin { return null; } + @Redirect(method = "writeChunkToNBT", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;"), + require = 1) + private NibbleArray grabEBSBlockMetaWrite(ExtendedBlockStorage instance) { + ebs = (IExtendedBlockStorageMixin) instance; + return null; + } + + @Redirect(method = "writeChunkToNBT", + at = @At(value = "FIELD", + target = "Lnet/minecraft/world/chunk/NibbleArray;data:[B", + ordinal = 0), + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;")), + require = 1) + private byte[] suppressEBSBlockMetaNibbleArrayAccess(NibbleArray instance) { + return null; + } + + @Redirect(method = "writeChunkToNBT", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/nbt/NBTTagCompound;setByteArray(Ljava/lang/String;[B)V", + ordinal = 0), + slice = @Slice(from = @At(value = "CONSTANT", + args = "stringValue=Data")), + require = 1) + private void redirectBlockMetaWrite(NBTTagCompound instance, String p_74773_1_, byte[] p_74773_2_) { + Hooks.writeBlockMetaToNBT(ebs, instance); + ebs = null; + } + @Redirect(method = "readChunkFromNBT", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NBTTagCompound;getByteArray(Ljava/lang/String;)[B", @@ -61,7 +89,7 @@ public abstract class AnvilChunkLoaderMixin { slice = @Slice(from = @At(value = "CONSTANT", args = "stringValue=Blocks")), require = 1) - private byte[] grabNBTBlockDataRead(NBTTagCompound instance, String p_74770_1_) { + private byte[] grabNBTBlockDataRead(NBTTagCompound instance, String key) { nbt = instance; return null; } @@ -71,7 +99,7 @@ public abstract class AnvilChunkLoaderMixin { target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;setBlockLSBArray([B)V"), require = 1) private void redirectBlockDataRead(ExtendedBlockStorage instance, byte[] p_76664_1_) { - Hooks.readChunkFromNbt((IExtendedBlockStorageMixin) instance, nbt); + Hooks.readBlockDataFromNBT((IExtendedBlockStorageMixin) instance, nbt); nbt = null; } @@ -83,7 +111,28 @@ public abstract class AnvilChunkLoaderMixin { args = "stringValue=Add", ordinal = 0)), require = 1) - private boolean suppressVanillaBlockDataRead(NBTTagCompound instance, String p_150297_1_, int p_150297_2_) { + private boolean suppressVanillaBlockDataRead(NBTTagCompound instance, String key, int type) { return false; } + + @Redirect(method = "readChunkFromNBT", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/nbt/NBTTagCompound;getByteArray(Ljava/lang/String;)[B", + ordinal = 0), + slice = @Slice(from = @At(value = "CONSTANT", + args = "stringValue=Data")), + require = 1) + private byte[] grabNBTBlockMetaRead(NBTTagCompound instance, String key) { + nbt = instance; + return null; + } + + @Redirect(method = "readChunkFromNBT", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;setBlockMetadataArray(Lnet/minecraft/world/chunk/NibbleArray;)V"), + require = 1) + private void redirectBlockMetaRead(ExtendedBlockStorage instance, NibbleArray p_76668_1_) { + Hooks.readBlockMetaFromNBT((IExtendedBlockStorageMixin)instance, nbt); + nbt = null; + } } diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/ExtendedBlockStorageMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/ExtendedBlockStorageMixin.java index 99f582a..57bc226 100644 --- a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/ExtendedBlockStorageMixin.java +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/ExtendedBlockStorageMixin.java @@ -1,16 +1,23 @@ package com.falsepattern.endlessids.mixin.mixins.common.blockitem.vanilla; +import com.falsepattern.endlessids.EndlessIDs; import com.falsepattern.endlessids.Hooks; +import com.falsepattern.endlessids.Tags; import com.falsepattern.endlessids.mixin.helpers.IExtendedBlockStorageMixin; +import lombok.val; +import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.block.Block; import net.minecraft.init.Blocks; +import net.minecraft.world.chunk.NibbleArray; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; @Mixin(ExtendedBlockStorage.class) @@ -19,8 +26,11 @@ public abstract class ExtendedBlockStorageMixin implements IExtendedBlockStorage private int blockRefCount; @Shadow private int tickRefCount; + @Shadow private byte[] blockLSBArray; + @Shadow private NibbleArray blockMetadataArray; private short[] lsbArray; private byte[] msbArray; + private short[] metaArray; @Override public void setBlockRefCount(int value) { @@ -42,12 +52,20 @@ public abstract class ExtendedBlockStorageMixin implements IExtendedBlockStorage return msbArray; } + @Override + public short[] getMetaArray() { + return metaArray; + } + @Inject(method = "", at = @At(value = "RETURN"), require = 1) private void init(CallbackInfo ci) { + blockLSBArray = null; + blockMetadataArray = null; lsbArray = new short[16 * 16 * 16]; msbArray = new byte[16 * 16 * 16]; + metaArray = new short[16 * 16 * 16]; } private int getID(int x, int y, int z) { @@ -99,6 +117,26 @@ public abstract class ExtendedBlockStorageMixin implements IExtendedBlockStorage setID(x, y, z, blockID); } + /** + * @author FalsePattern + * @reason meta ID extension + */ + @Overwrite + public int getExtBlockMetadata(int x, int y, int z) { + int index = y << 8 | z << 4 | x; + return metaArray[index] & 0xFFFF; + } + + /** + * @author FalsePattern + * @reason meta ID extension + */ + @Overwrite + public void setExtBlockMetadata(int x, int y, int z, int meta) { + int index = y << 8 | z << 4 | x; + metaArray[index] = (short)(meta & 0xFFFF); + } + /** * @author FalsePattern * @reason Direct port from dumped code @@ -107,4 +145,54 @@ public abstract class ExtendedBlockStorageMixin implements IExtendedBlockStorage public void removeInvalidBlocks() { Hooks.removeInvalidBlocksHook(this); } + + private UnsupportedOperationException emergencyCrash() { + val crashMSG = "A mod that is incompatible with " + Tags.MODNAME + " has tried to access the block array of a" + + " chunk like in vanilla! Crashing in fear of potential world corruption!\n" + + "Please report this issue on https://github.com/FalsePattern/EndlessIDs ASAP!"; + EndlessIDs.LOG.fatal(crashMSG); + return new UnsupportedOperationException(crashMSG); + } + + @Inject(method = {"getBlockMSBArray", "createBlockMSBArray"}, + at = @At("HEAD")) + private void crashMSBArray(CallbackInfoReturnable cir) { + throw emergencyCrash(); + } + + @Inject(method = "clearMSBArray", + at = @At("HEAD")) + private void crashMSBArray(CallbackInfo ci) { + throw emergencyCrash(); + } + + @Inject(method = "setBlockMSBArray", + at = @At("HEAD")) + private void crashMSBArray(NibbleArray p_76673_1_, CallbackInfo ci) { + throw emergencyCrash(); + } + + @Inject(method = "setBlockLSBArray", + at = @At("HEAD")) + private void crashLSBArray(byte[] p_76664_1_, CallbackInfo ci) { + throw emergencyCrash(); + } + + @Inject(method = "getBlockLSBArray", + at = @At("HEAD")) + private void crashLSBArray(CallbackInfoReturnable cir) { + throw emergencyCrash(); + } + + @Inject(method = "setBlockMetadataArray", + at = @At("HEAD")) + private void crashMetadataArray(NibbleArray p_76668_1_, CallbackInfo ci) { + throw emergencyCrash(); + } + + @Inject(method = "getMetadataArray", + at = @At("HEAD")) + private void crashMetadataArray(CallbackInfoReturnable cir) { + throw emergencyCrash(); + } } diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/S21PacketChunkDataMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/S21PacketChunkDataMixin.java index b685e91..e762770 100644 --- a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/S21PacketChunkDataMixin.java +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/S21PacketChunkDataMixin.java @@ -1,5 +1,6 @@ package com.falsepattern.endlessids.mixin.mixins.common.blockitem.vanilla; +import com.falsepattern.endlessids.EndlessIDs; import com.falsepattern.endlessids.Hooks; import com.falsepattern.endlessids.constants.ExtendedConstants; import com.falsepattern.endlessids.constants.VanillaConstants; @@ -7,20 +8,100 @@ import com.falsepattern.endlessids.mixin.helpers.IExtendedBlockStorageMixin; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyConstant; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import net.minecraft.network.play.server.S21PacketChunkData; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.NibbleArray; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; @Mixin(S21PacketChunkData.class) public abstract class S21PacketChunkDataMixin { + @Redirect(method = "func_149269_a", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getBlockMSBArray()Lnet/minecraft/world/chunk/NibbleArray;", + ordinal = 0), + require = 1) + private static NibbleArray noMSB(ExtendedBlockStorage instance) { + return null; + } + + @Inject(method = "func_149269_a", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getBlockLSBArray()[B"), + locals = LocalCapture.CAPTURE_FAILHARD, + require = 1) + private static void hookGetBlockData(Chunk p_149269_0_, boolean p_149269_1_, int p_149269_2_, CallbackInfoReturnable cir, int j, ExtendedBlockStorage[] aextendedblockstorage, int k, S21PacketChunkData.Extracted extracted, byte[] abyte, int l) { + Hooks.getBlockData((IExtendedBlockStorageMixin) aextendedblockstorage[l], abyte, j); + } + + private static byte[] fakeArray; @Redirect(method = "func_149269_a", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getBlockLSBArray()[B"), require = 1) - private static byte[] hookGetBlockData(ExtendedBlockStorage instance) { - return Hooks.getBlockData((IExtendedBlockStorageMixin) instance); + private static byte[] hookGetBlockDataFakeArray(ExtendedBlockStorage instance) { + return fakeArray != null ? fakeArray : (fakeArray = new byte[16 * 16 * 16 * ExtendedConstants.bytesPerID]); + } + + @Redirect(method = "func_149269_a", + at = @At(value = "INVOKE", + target = "Ljava/lang/System;arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V", + ordinal = 0), + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getBlockLSBArray()[B")), + require = 1) + private static void hookGetBlockDataNoCopy(Object src, int srcPos, Object dest, int destPos, int length) { + + } + + @Inject(method = "func_149269_a", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;"), + locals = LocalCapture.CAPTURE_FAILHARD, + require = 1) + private static void hookGetBlockMeta(Chunk p_149269_0_, boolean p_149269_1_, int p_149269_2_, CallbackInfoReturnable cir, int j, ExtendedBlockStorage[] aextendedblockstorage, int k, S21PacketChunkData.Extracted extracted, byte[] abyte, int l) { + Hooks.getBlockMeta((IExtendedBlockStorageMixin) aextendedblockstorage[l], abyte, j); + } + + private static NibbleArray fakeNarray; + + @Redirect(method = "func_149269_a", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;"), + require = 1) + private static NibbleArray hookGetBlockMetaNoArray(ExtendedBlockStorage instance) { + return fakeNarray != null ? fakeNarray : (fakeNarray = new NibbleArray(null, 0)); + } + + @Redirect(method = "func_149269_a", + at = @At(value = "FIELD", + target = "Lnet/minecraft/world/chunk/NibbleArray;data:[B", + args = "array=length"), + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;"), + to = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getBlocklightArray()Lnet/minecraft/world/chunk/NibbleArray;")), + expect = 2, + require = 2) + private static int hookGetBlockMetaFakeBytesLength(byte[] array) { + return 16 * 16 * 16 * (ExtendedConstants.nibblesPerMetadata >>> 1); + } + + @Redirect(method = "func_149269_a", + at = @At(value = "INVOKE", + target = "Ljava/lang/System;arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V", + ordinal = 0), + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;getMetadataArray()Lnet/minecraft/world/chunk/NibbleArray;")), + require = 1) + private static void hookGetBlockMetaNoCopy(Object src, int srcPos, Object dest, int destPos, int length) { + } @ModifyConstant(method = "readPacketData", diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/S22PacketMultiBlockChangeMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/S22PacketMultiBlockChangeMixin.java index 9858cb0..7197e48 100644 --- a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/S22PacketMultiBlockChangeMixin.java +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/vanilla/S22PacketMultiBlockChangeMixin.java @@ -1,5 +1,7 @@ package com.falsepattern.endlessids.mixin.mixins.common.blockitem.vanilla; +import com.falsepattern.endlessids.constants.ExtendedConstants; +import com.falsepattern.endlessids.constants.VanillaConstants; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Constant; @@ -18,13 +20,12 @@ public abstract class S22PacketMultiBlockChangeMixin { private int id; private int meta; - //TODO magic number @ModifyConstant(method = "(I[SLnet/minecraft/world/chunk/Chunk;)V", - constant = @Constant(intValue = 4, + constant = @Constant(intValue = 2 + VanillaConstants.bytesPerIDPlusMetadata, ordinal = 0), require = 1) private int extend1(int constant) { - return 6; + return 2 + ExtendedConstants.bytesPerIDPlusMetadata; } @Redirect(method = "(I[SLnet/minecraft/world/chunk/Chunk;)V", @@ -46,6 +47,6 @@ public abstract class S22PacketMultiBlockChangeMixin { private void hackWrite(DataOutputStream instance, int value) throws IOException { instance.writeShort(id & 0xFFFF); instance.writeByte((id >>> 16) & 0xFF); - instance.writeByte(meta); + instance.writeShort(meta); } } diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/worldedit/BaseBlockMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/worldedit/BaseBlockMixin.java index ff0ef65..a983026 100644 --- a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/worldedit/BaseBlockMixin.java +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/blockitem/worldedit/BaseBlockMixin.java @@ -4,10 +4,12 @@ import com.falsepattern.endlessids.constants.ExtendedConstants; import com.sk89q.worldedit.blocks.BaseBlock; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; @Mixin(value = BaseBlock.class, remap = false) public abstract class BaseBlockMixin { + @Shadow private short data; private int idExtended; /** @@ -35,4 +37,29 @@ public abstract class BaseBlockMixin { return idExtended; } + /** + * @author FalsePattern + * @reason ID limit extension + */ + @Overwrite + public int getData() { + return this.data & 0xFFFF; + } + + + /** + * @author FalsePattern + * @reason ID limit extension + */ + @Overwrite + protected final void internalSetData(int data) { + if (data > 0xFFFF) { + throw new IllegalArgumentException("Can't have a block data value above 65535 (" + data + " given)"); + } else if (data < -1) { + throw new IllegalArgumentException("Can't have a block data value below -1"); + } else { + this.data = (short)data; + } + } + } diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/BlockCompressedPoweredMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/BlockCompressedPoweredMixin.java new file mode 100644 index 0000000..8afa615 --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/BlockCompressedPoweredMixin.java @@ -0,0 +1,21 @@ +package com.falsepattern.endlessids.mixin.mixins.common.redstone; + +import com.falsepattern.endlessids.config.GeneralConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.block.BlockCompressedPowered; +import net.minecraft.world.IBlockAccess; + +@Mixin(BlockCompressedPowered.class) +public abstract class BlockCompressedPoweredMixin { + @Inject(method = "isProvidingWeakPower", + at = @At("HEAD"), + cancellable = true, + require = 1) + private void bigRedstone(IBlockAccess p_149709_1_, int p_149709_2_, int p_149709_3_, int p_149709_4_, int p_149709_5_, CallbackInfoReturnable cir) { + cir.setReturnValue(GeneralConfig.maxRedstone); + } +} diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/BlockRedstoneDiodeMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/BlockRedstoneDiodeMixin.java new file mode 100644 index 0000000..545be8e --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/BlockRedstoneDiodeMixin.java @@ -0,0 +1,20 @@ +package com.falsepattern.endlessids.mixin.mixins.common.redstone; + +import com.falsepattern.endlessids.config.GeneralConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.block.BlockRedstoneDiode; +import net.minecraft.world.IBlockAccess; + +@Mixin(BlockRedstoneDiode.class) +public abstract class BlockRedstoneDiodeMixin { + @Inject(method = "func_149904_f", + at = @At("HEAD"), + cancellable = true) + private void bigRedstone(IBlockAccess p_149904_1_, int p_149904_2_, int p_149904_3_, int p_149904_4_, int p_149904_5_, CallbackInfoReturnable cir) { + cir.setReturnValue(GeneralConfig.maxRedstone); + } +} diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/BlockRedstoneTorchMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/BlockRedstoneTorchMixin.java new file mode 100644 index 0000000..d627391 --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/BlockRedstoneTorchMixin.java @@ -0,0 +1,18 @@ +package com.falsepattern.endlessids.mixin.mixins.common.redstone; + +import com.falsepattern.endlessids.config.GeneralConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +import net.minecraft.block.BlockRedstoneTorch; + +@Mixin(BlockRedstoneTorch.class) +public abstract class BlockRedstoneTorchMixin { + @ModifyConstant(method = "isProvidingWeakPower", + constant = @Constant(intValue = 15), + require = 1) + private int bigRedstone(int original) { + return GeneralConfig.maxRedstone; + } +} diff --git a/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/WorldMixin.java b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/WorldMixin.java new file mode 100644 index 0000000..ed0fff8 --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/mixin/mixins/common/redstone/WorldMixin.java @@ -0,0 +1,18 @@ +package com.falsepattern.endlessids.mixin.mixins.common.redstone; + +import com.falsepattern.endlessids.config.GeneralConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +import net.minecraft.world.World; + +@Mixin(World.class) +public abstract class WorldMixin { + @ModifyConstant(method = {"getStrongestIndirectPower", "getBlockPowerInput"}, + constant = @Constant(intValue = 15), + require = 8) + private int bigRedstone(int constant) { + return GeneralConfig.maxRedstone; + } +} diff --git a/src/main/java/com/falsepattern/endlessids/mixin/plugin/Mixin.java b/src/main/java/com/falsepattern/endlessids/mixin/plugin/Mixin.java index aa636ea..9d09f40 100644 --- a/src/main/java/com/falsepattern/endlessids/mixin/plugin/Mixin.java +++ b/src/main/java/com/falsepattern/endlessids/mixin/plugin/Mixin.java @@ -242,6 +242,19 @@ public enum Mixin implements IMixin { //endregion potion //endregion client + //region redstone + //region common + BlockCompressedPoweredMixin(Side.COMMON, condition(() -> GeneralConfig.extendBlockItem && GeneralConfig.extendRedstone), "redstone.BlockCompressedPoweredMixin"), + BlockRedstoneDiodeMixin(Side.COMMON, condition(() -> GeneralConfig.extendBlockItem && GeneralConfig.extendRedstone), "redstone.BlockRedstoneDiodeMixin"), + BlockRedstoneTorchMixin(Side.COMMON, condition(() -> GeneralConfig.extendBlockItem && GeneralConfig.extendRedstone), "redstone.BlockRedstoneTorchMixin"), + RedstoneWorldMixin(Side.COMMON, condition(() -> GeneralConfig.extendBlockItem && GeneralConfig.extendRedstone), "redstone.WorldMixin"), + //endregion common + //region client + BlockRedstoneWireMixin(Side.CLIENT, condition(() -> GeneralConfig.extendBlockItem && GeneralConfig.extendRedstone), "redstone.BlockRedstoneWireMixin"), + RedstoneRenderBlocksMixin(Side.CLIENT, condition(() -> GeneralConfig.extendBlockItem && GeneralConfig.extendRedstone), "redstone.RenderBlocksMixin"), + //endregion client + //endregion redstone + ; @Getter private final Side side;