diff --git a/build.gradle b/build.gradle index 5ee59bb..63cdc4b 100644 --- a/build.gradle +++ b/build.gradle @@ -134,6 +134,7 @@ dependencies { compileOnly rfg.deobf( "curse.maven:GeographiCraft-76544:2497541" ) compileOnly rfg.deobf( "curse.maven:mystcraft-224599:2958490" ) compileOnly rfg.deobf( "curse.maven:HammerCore-247401:3366814" ) + compileOnly rfg.deobf( "curse.maven:phosphor-forge-318255:2747710" ) // Advanced Rocketry 1.7.0 compileOnly rfg.deobf( "curse.maven:AdvancedRocketry-236542:3015079" ) // Advanced Rocketry 2.0.0 diff --git a/src/main/java/org/dimdev/jeid/core/JEIDMixinPlugin.java b/src/main/java/org/dimdev/jeid/core/JEIDMixinPlugin.java new file mode 100644 index 0000000..4fd3673 --- /dev/null +++ b/src/main/java/org/dimdev/jeid/core/JEIDMixinPlugin.java @@ -0,0 +1,78 @@ +package org.dimdev.jeid.core; + +import com.google.common.collect.ImmutableList; +import net.minecraftforge.fml.common.Loader; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class JEIDMixinPlugin implements IMixinConfigPlugin { + + private final List matchingTarget = ImmutableList.of( + "net.minecraft.world.chunk.Chunk", + "net.minecraft.world.chunk.storage.AnvilChunkLoader", + "net.minecraft.network.play.server.SPacketChunkData"); + private final List matchingMixin = ImmutableList.of( + "org.dimdev.jeid.mixin.core.world.MixinChunk", + "me.jellysquid.mods.phosphor.mixins.lighting.common.MixinChunk", + "org.dimdev.jeid.mixin.core.world.MixinAnvilChunkLoader", + "me.jellysquid.mods.phosphor.mixins.lighting.common.MixinAnvilChunkLoader", + "org.dimdev.jeid.mixin.core.network.MixinSPacketChunkData", + "me.jellysquid.mods.phosphor.mixins.lighting.common.MixinSPacketChunkData", + "me.jellysquid.mods.phosphor.mixins.lighting.common.MixinChunk$Sponge", + "me.jellysquid.mods.phosphor.mixins.lighting.common.MixinChunk$Vanilla"); + + @Override + public void onLoad(String mixinPackage) { + + } + + @Override + public String getRefMapperConfig() { + return "mixins.jeid.refmap.json"; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + if (Loader.isModLoaded("phosphor-lighting")) { + if (matchingTarget.contains(targetClassName) && matchingMixin.contains(mixinClassName)) { + return false; + } + } + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override + public List getMixins() { + if (Loader.isModLoaded("phosphor-lighting")){ + return ImmutableList.of( + "org.dimdev.jeid.mixin.modsupport.phosphor.MixinChunk", + "org.dimdev.jeid.mixin.modsupport.phosphor.MixinAnvilChunkLoader", + "org.dimdev.jeid.mixin.modsupport.phosphor.MixinSPacketChunkData", + "org.dimdev.jeid.mixin.modsupport.phosphor.MixinChunk$Vanilla", + "org.dimdev.jeid.mixin.modsupport.phosphor.MixinChunk$Sponge"); + } + + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } +} diff --git a/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinAnvilChunkLoader.java b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinAnvilChunkLoader.java new file mode 100644 index 0000000..486f703 --- /dev/null +++ b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinAnvilChunkLoader.java @@ -0,0 +1,115 @@ +package org.dimdev.jeid.mixin.modsupport.phosphor; + +import com.llamalad7.mixinextras.sugar.Local; +import me.jellysquid.mods.phosphor.api.IChunkLightingData; +import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; +import me.jellysquid.mods.phosphor.mod.world.lighting.LightingHooks; +import net.minecraft.nbt.NBTTagCompound; +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; +import org.dimdev.jeid.JEID; +import org.dimdev.jeid.ducks.INewBlockStateContainer; +import org.dimdev.jeid.ducks.INewChunk; +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.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(AnvilChunkLoader.class) +public class MixinAnvilChunkLoader { + /** + * @reason Read palette from NBT for JustEnoughIDs BlockStateContainers. + */ + @Inject(method = "readChunkFromNBT", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NBTTagCompound;getByteArray(Ljava/lang/String;)[B", ordinal = 0)) + private void reid$readPaletteNBT(CallbackInfoReturnable cir, @Local(ordinal = 1) NBTTagCompound storageNBT, @Local ExtendedBlockStorage extendedBlockStorage) { + int[] palette = storageNBT.hasKey("Palette", 11) ? storageNBT.getIntArray("Palette") : null; + ((INewBlockStateContainer) extendedBlockStorage.getData()).setTemporaryPalette(palette); + NibbleArray add2 = storageNBT.hasKey("Add2", 7) ? new NibbleArray(storageNBT.getByteArray("Add2")) : null; + ((INewBlockStateContainer) extendedBlockStorage.getData()).setLegacyAdd2(add2); + } + + /** + * @reason Write palette to NBT for JustEnoughIDs BlockStateContainers. + */ + @Inject(method = "writeChunkToNBT", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/chunk/BlockStateContainer;getDataForNBT([BLnet/minecraft/world/chunk/NibbleArray;)Lnet/minecraft/world/chunk/NibbleArray;", ordinal = 0)) + private void reid$writePaletteNBT(CallbackInfo ci, @Local ExtendedBlockStorage extendedBlockStorage, @Local(ordinal = 1) NBTTagCompound storageNBT) { + int[] palette = ((INewBlockStateContainer) extendedBlockStorage.getData()).getTemporaryPalette(); + if (palette != null) storageNBT.setIntArray("Palette", palette); + } + + /** + * @reason Read int biome array from NBT if it's there. + */ + @Inject(method = "readChunkFromNBT", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NBTTagCompound;hasKey(Ljava/lang/String;I)Z", ordinal = 1)) + private void reid$readBiomeArray(World world, NBTTagCompound nbt, CallbackInfoReturnable cir, @Local Chunk chunk) { + INewChunk newChunk = (INewChunk) chunk; + if (nbt.hasKey("Biomes", 11)) { + newChunk.setIntBiomeArray(nbt.getIntArray("Biomes")); + } else { + // Convert old chunks + int[] intBiomeArray = new int[256]; + int index = 0; + for (byte b : nbt.getByteArray("Biomes")) { + intBiomeArray[index++] = b & 0xFF; + } + newChunk.setIntBiomeArray(intBiomeArray); + } + } + + /** + * @reason Save the correct biome array type + */ + @Redirect(method = "writeChunkToNBT", + slice = @Slice( + id = "nbtBiomes", + from = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NBTTagCompound;setTag(Ljava/lang/String;Lnet/minecraft/nbt/NBTBase;)V"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/Chunk;setHasEntities(Z)V") + ), at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NBTTagCompound;setByteArray(Ljava/lang/String;[B)V", ordinal = 0, slice = "nbtBiomes")) + private void reid$writeBiomeArray(NBTTagCompound instance, String key, byte[] value, Chunk chunkIn) { + if (!key.equals("Biomes")) { + throw new AssertionError(JEID.MODID + " :: Sliced target setByteArray isn't \"Biomes\""); + } + instance.setIntArray(key, ((INewChunk) chunkIn).getIntBiomeArray()); + } + + /** + * @reason Disable default biome array save logic. + */ + @Redirect(method = "writeChunkToNBT", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/Chunk;getBiomeArray()[B", ordinal = 0)) + private byte[] reid$defaultWriteBiomeArray(Chunk chunk) { + return new byte[0]; + } + + @Inject( + method = {"saveChunk"}, + at = {@At("HEAD")} + ) + private void onConstructed(World world, Chunk chunkIn, CallbackInfo callbackInfo) { + ((ILightingEngineProvider)world).getLightingEngine().processLightUpdates(); + } + + @Inject( + method = {"readChunkFromNBT"}, + at = {@At("RETURN")} + ) + private void onReadChunkFromNBT(World world, NBTTagCompound compound, CallbackInfoReturnable cir) { + Chunk chunk = (Chunk)cir.getReturnValue(); + LightingHooks.readNeighborLightChecksFromNBT(chunk, compound); + ((IChunkLightingData)chunk).setLightInitialized(compound.getBoolean("LightPopulated")); + } + + @Inject( + method = {"writeChunkToNBT"}, + at = {@At("RETURN")} + ) + private void onWriteChunkToNBT(Chunk chunk, World world, NBTTagCompound compound, CallbackInfo ci) { + LightingHooks.writeNeighborLightChecksToNBT(chunk, compound); + compound.setBoolean("LightPopulated", ((IChunkLightingData)chunk).isLightInitialized()); + } +} diff --git a/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinChunk$Sponge.java b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinChunk$Sponge.java new file mode 100644 index 0000000..1c82295 --- /dev/null +++ b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinChunk$Sponge.java @@ -0,0 +1,129 @@ +package org.dimdev.jeid.mixin.modsupport.phosphor; + +import me.jellysquid.mods.phosphor.mod.world.lighting.LightingHooks; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import org.spongepowered.asm.mixin.Dynamic; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; + +@Mixin(Chunk.class) +public abstract class MixinChunk$Sponge { + private static final String SET_BLOCK_STATE_SPONGE = "bridge$setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/block/state/IBlockState;Lorg/spongepowered/api/world/BlockChangeFlag;)Lnet/minecraft/block/state/IBlockState;"; + @Shadow + @Final + private World world; + @Final + @Shadow + public int x; + @Final + @Shadow + public int z; + + private static final int WIZARD_MAGIC = 694698818; + + public MixinChunk$Sponge() { + } + + @Redirect( + method = {"bridge$setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/block/state/IBlockState;Lorg/spongepowered/api/world/BlockChangeFlag;)Lnet/minecraft/block/state/IBlockState;"}, + at = @At( + value = "NEW", + args = {"class=net/minecraft/world/chunk/storage/ExtendedBlockStorage"} + ), + expect = 0 + ) + @Dynamic + private ExtendedBlockStorage setBlockStateCreateSectionSponge(int y, boolean storeSkylight) { + return this.initSection(y, storeSkylight); + } + + private ExtendedBlockStorage initSection(int y, boolean storeSkylight) { + ExtendedBlockStorage storage = new ExtendedBlockStorage(y, storeSkylight); + LightingHooks.initSkylightForSection(this.world, new Chunk(this.world, this.x, this.z), storage); + return storage; + } + + @ModifyVariable( + method = {"bridge$setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/block/state/IBlockState;Lorg/spongepowered/api/world/BlockChangeFlag;)Lnet/minecraft/block/state/IBlockState;"}, + at = @At( + value = "LOAD", + ordinal = 0 + ), + index = 14, + name = {"requiresNewLightCalculations"}, + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;get(III)Lnet/minecraft/block/state/IBlockState;" + ), + to = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;generateSkylightMap()V" + ) + ), + allow = 1 + ) + @Dynamic + private boolean setBlockStateInjectGenerateSkylightMapVanilla(boolean generateSkylight) { + return false; + } + + @ModifyVariable( + method = {"bridge$setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/block/state/IBlockState;Lorg/spongepowered/api/world/BlockChangeFlag;)Lnet/minecraft/block/state/IBlockState;"}, + at = @At( + value = "LOAD", + ordinal = 1 + ), + index = 13, + name = {"newBlockLightOpacity"}, + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;relightBlock(III)V", + ordinal = 1 + ), + to = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;propagateSkylightOcclusion(II)V" + ) + ), + allow = 1 + ) + @Dynamic + private int setBlockStatePreventPropagateSkylightOcclusion1(int generateSkylight) { + return 694698818; + } + + @ModifyVariable( + method = {"bridge$setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/block/state/IBlockState;Lorg/spongepowered/api/world/BlockChangeFlag;)Lnet/minecraft/block/state/IBlockState;"}, + at = @At( + value = "LOAD", + ordinal = 0 + ), + index = 24, + name = {"postNewBlockLightOpacity"}, + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;relightBlock(III)V", + ordinal = 1 + ), + to = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;propagateSkylightOcclusion(II)V" + ) + ), + allow = 1 + ) + @Dynamic + private int setBlockStatePreventPropagateSkylightOcclusion2(int generateSkylight) { + return 694698818; + } +} diff --git a/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinChunk$Vanilla.java b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinChunk$Vanilla.java new file mode 100644 index 0000000..ea2b5df --- /dev/null +++ b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinChunk$Vanilla.java @@ -0,0 +1,124 @@ +package org.dimdev.jeid.mixin.modsupport.phosphor; + +import me.jellysquid.mods.phosphor.mod.world.lighting.LightingHooks; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; + +@Mixin(Chunk.class) +public abstract class MixinChunk$Vanilla { + private static final String SET_BLOCK_STATE_VANILLA = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Lnet/minecraft/block/state/IBlockState;"; + @Shadow + @Final + private World world; + @Final + @Shadow + public int x; + @Final + @Shadow + public int z; + private static final int WIZARD_MAGIC = 694698818; + + public MixinChunk$Vanilla() { + } + + @Redirect( + method = {"setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Lnet/minecraft/block/state/IBlockState;"}, + at = @At( + value = "NEW", + args = {"class=net/minecraft/world/chunk/storage/ExtendedBlockStorage"} + ), + expect = 0 + ) + private ExtendedBlockStorage setBlockStateCreateSectionVanilla(int y, boolean storeSkylight) { + return this.initSection(y, storeSkylight); + } + + private ExtendedBlockStorage initSection(int y, boolean storeSkylight) { + ExtendedBlockStorage storage = new ExtendedBlockStorage(y, storeSkylight); + LightingHooks.initSkylightForSection(this.world, new Chunk(this.world, this.x, this.z), storage); + return storage; + } + + @ModifyVariable( + method = {"setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Lnet/minecraft/block/state/IBlockState;"}, + at = @At( + value = "STORE", + ordinal = 1 + ), + index = 13, + name = {"flag"}, + slice = @Slice( + from = @At( + value = "FIELD", + target = "Lnet/minecraft/world/chunk/Chunk;storageArrays:[Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;", + ordinal = 1 + ), + to = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;set(IIILnet/minecraft/block/state/IBlockState;)V" + ) + ), + allow = 1 + ) + private boolean setBlockStateInjectGenerateSkylightMapVanilla(boolean generateSkylight) { + return false; + } + + @ModifyVariable( + method = {"setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Lnet/minecraft/block/state/IBlockState;"}, + at = @At( + value = "LOAD", + ordinal = 0 + ), + index = 11, + name = {"k1"}, + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;relightBlock(III)V", + ordinal = 1 + ), + to = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;propagateSkylightOcclusion(II)V" + ) + ), + allow = 1 + ) + private int setBlockStatePreventPropagateSkylightOcclusion1(int generateSkylight) { + return 694698818; + } + + @ModifyVariable( + method = {"setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Lnet/minecraft/block/state/IBlockState;"}, + at = @At( + value = "LOAD", + ordinal = 1 + ), + index = 14, + name = {"j1"}, + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;relightBlock(III)V", + ordinal = 1 + ), + to = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;propagateSkylightOcclusion(II)V" + ) + ), + allow = 1 + ) + private int setBlockStatePreventPropagateSkylightOcclusion2(int generateSkylight) { + return 694698818; + } +} diff --git a/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinChunk.java b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinChunk.java new file mode 100644 index 0000000..10cba0e --- /dev/null +++ b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinChunk.java @@ -0,0 +1,349 @@ +package org.dimdev.jeid.mixin.modsupport.phosphor; + +import com.llamalad7.mixinextras.sugar.Local; +import me.jellysquid.mods.phosphor.api.IChunkLighting; +import me.jellysquid.mods.phosphor.api.IChunkLightingData; +import me.jellysquid.mods.phosphor.api.ILightingEngine; +import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; +import me.jellysquid.mods.phosphor.mod.world.WorldChunkSlice; +import me.jellysquid.mods.phosphor.mod.world.lighting.LightingHooks; +import net.minecraft.block.state.IBlockState; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.EnumSkyBlock; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import org.dimdev.jeid.biome.BiomeError; +import org.dimdev.jeid.ducks.INewChunk; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Arrays; + +@Mixin(value = {Chunk.class},priority = 1100) +public abstract class MixinChunk implements IChunkLighting, IChunkLightingData, ILightingEngineProvider, INewChunk { + @Unique + private static final byte ERROR_BIOME_ID = (byte) Biome.REGISTRY.getIDForObject(BiomeError.getInstance()); + @Unique + private static final byte[] EMPTY_BLOCK_BIOME_ARRAY = new byte[256]; + @Unique + private final int[] intBiomeArray = generateIntBiomeArray(); + + @Unique + private static int[] generateIntBiomeArray() { + int[] arr = new int[256]; + Arrays.fill(arr, -1); + return arr; + } + + @Override + public int[] getIntBiomeArray() { + return intBiomeArray; + } + + @Override + public void setIntBiomeArray(int[] intBiomeArray) { + System.arraycopy(intBiomeArray, 0, this.intBiomeArray, 0, this.intBiomeArray.length); + } + + @Inject(method = "getBiomeArray", at = @At(value = "RETURN"), cancellable = true) + private void reid$returnErrorBiomeArray(CallbackInfoReturnable cir) { + byte[] arr = new byte[256]; + Arrays.fill(arr, ERROR_BIOME_ID); + cir.setReturnValue(arr); + } + + @Dynamic("Read biome id from int biome array to int k") + @ModifyVariable(method = "getBiome", at = @At(value = "STORE", ordinal = 0), name = "k") + private int reid$fromIntBiomeArray(int original, @Local(name = "i") int i, @Local(name = "j") int j) { + return this.intBiomeArray[j << 4 | i]; + } + + /** + * Compatibility for mods that don't initialize the chunk's biome array on generation (e.g. Chunk-Pregenerator) + * + * @reason Use intBiomeArray's default value. + */ + @ModifyConstant(method = "getBiome", constant = @Constant(intValue = 255, ordinal = 1)) + private int reid$modifyDefaultId(int original) { + return -1; + } + + @Inject(method = "getBiome", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/biome/Biome;getIdForBiome(Lnet/minecraft/world/biome/Biome;)I")) + private void reid$toIntBiomeArray(CallbackInfoReturnable cir, @Local(name = "i") int i, @Local(name = "j") int j, @Local(name = "k") int k) { + this.intBiomeArray[j << 4 | i] = k; + } + + /** + * @reason Disable default biome array write logic. + */ + @Redirect(method = "getBiome", at = @At(value = "FIELD", target = "Lnet/minecraft/world/chunk/Chunk;blockBiomeArray:[B", opcode = Opcodes.GETFIELD, ordinal = 1)) + private byte[] reid$defaultWriteBiomeArray(Chunk instance) { + return EMPTY_BLOCK_BIOME_ARRAY; + } + + + private static final EnumFacing[] HORIZONTAL; + @Shadow + @Final + private ExtendedBlockStorage[] storageArrays; + @Shadow + private boolean dirty; + @Shadow + @Final + private int[] heightMap; + @Shadow + private int heightMapMinimum; + @Shadow + @Final + private int[] precipitationHeightMap; + @Shadow + @Final + private World world; + @Shadow + private boolean isTerrainPopulated; + @Final + @Shadow + private boolean[] updateSkylightColumns; + @Final + @Shadow + public int x; + @Final + @Shadow + public int z; + @Shadow + private boolean isGapLightingUpdated; + private short[] neighborLightChecks; + private boolean isLightInitialized; + private ILightingEngine lightingEngine; + + @Shadow + public abstract TileEntity getTileEntity(BlockPos var1, Chunk.EnumCreateEntityType var2); + + @Shadow + public abstract IBlockState getBlockState(BlockPos var1); + + @Shadow + protected abstract int getBlockLightOpacity(int var1, int var2, int var3); + + @Shadow + public abstract boolean canSeeSky(BlockPos var1); + + @Inject( + method = {""}, + at = {@At("RETURN")} + ) + private void onConstructed(CallbackInfo ci) { + this.lightingEngine = ((ILightingEngineProvider)this.world).getLightingEngine(); + } + + @Inject( + method = {"getLightSubtracted"}, + at = {@At("HEAD")} + ) + private void onGetLightSubtracted(BlockPos pos, int amount, CallbackInfoReturnable cir) { + this.lightingEngine.processLightUpdates(); + } + + @Inject( + method = {"onLoad"}, + at = {@At("RETURN")} + ) + private void onLoad(CallbackInfo ci) { + LightingHooks.scheduleRelightChecksForChunkBoundaries(this.world, new Chunk(this.world, this.x, this.z)); + } + + @Redirect( + method = {"setLightFor"}, + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/Chunk;generateSkylightMap()V" + ), + expect = 0 + ) + private void setLightForRedirectGenerateSkylightMap(Chunk chunk, EnumSkyBlock type, BlockPos pos, int value) { + LightingHooks.initSkylightForSection(this.world, new Chunk(this.world,this.x,this.z), this.storageArrays[pos.getY() >> 4]); + } + + @Overwrite + private void relightBlock(int x, int y, int z) { + int i = this.heightMap[z << 4 | x] & 255; + int j = i; + if (y > i) { + j = y; + } + + while(j > 0 && this.getBlockLightOpacity(x, j - 1, z) == 0) { + --j; + } + + if (j != i) { + this.heightMap[z << 4 | x] = j; + if (this.world.provider.hasSkyLight()) { + LightingHooks.relightSkylightColumn(this.world, new Chunk(this.world,this.x,this.z), x, z, i, j); + } + + int l1 = this.heightMap[z << 4 | x]; + if (l1 < this.heightMapMinimum) { + this.heightMapMinimum = l1; + } + } + + } + + @Overwrite + public int getLightFor(EnumSkyBlock type, BlockPos pos) { + this.lightingEngine.processLightUpdatesForType(type); + return this.getCachedLightFor(type, pos); + } + + @Overwrite + public void checkLight() { + this.isTerrainPopulated = true; + LightingHooks.checkChunkLighting(new Chunk(this.world,this.x,this.z), this.world); + } + + @Overwrite + private void recheckGaps(boolean onlyOne) { + this.world.profiler.startSection("recheckGaps"); + WorldChunkSlice slice = new WorldChunkSlice(this.world, this.x, this.z); + if (this.world.isAreaLoaded(new BlockPos(this.x * 16 + 8, 0, this.z * 16 + 8), 16)) { + for(int x = 0; x < 16; ++x) { + for(int z = 0; z < 16; ++z) { + if (this.recheckGapsForColumn(slice, x, z) && onlyOne) { + this.world.profiler.endSection(); + return; + } + } + } + + this.isGapLightingUpdated = false; + } + + this.world.profiler.endSection(); + } + + private boolean recheckGapsForColumn(WorldChunkSlice slice, int x, int z) { + int i = x + z * 16; + if (this.updateSkylightColumns[i]) { + this.updateSkylightColumns[i] = false; + int height = this.getHeightValue(x, z); + int x1 = this.x * 16 + x; + int z1 = this.z * 16 + z; + int max = this.recheckGapsGetLowestHeight(slice, x1, z1); + this.recheckGapsSkylightNeighborHeight(slice, x1, z1, height, max); + return true; + } else { + return false; + } + } + + private int recheckGapsGetLowestHeight(WorldChunkSlice slice, int x, int z) { + int max = Integer.MAX_VALUE; + EnumFacing[] var5 = HORIZONTAL; + int var6 = var5.length; + + for(int var7 = 0; var7 < var6; ++var7) { + EnumFacing facing = var5[var7]; + int j = x + facing.getXOffset(); + int k = z + facing.getZOffset(); + max = Math.min(max, slice.getChunkFromWorldCoords(j, k).getLowestHeight()); + } + + return max; + } + + private void recheckGapsSkylightNeighborHeight(WorldChunkSlice slice, int x, int z, int height, int max) { + this.checkSkylightNeighborHeight(slice, x, z, max); + EnumFacing[] var6 = HORIZONTAL; + int var7 = var6.length; + + for(int var8 = 0; var8 < var7; ++var8) { + EnumFacing facing = var6[var8]; + int j = x + facing.getXOffset(); + int k = z + facing.getZOffset(); + this.checkSkylightNeighborHeight(slice, j, k, height); + } + + } + + private void checkSkylightNeighborHeight(WorldChunkSlice slice, int x, int z, int maxValue) { + int i = slice.getChunkFromWorldCoords(x, z).getHeightValue(x & 15, z & 15); + if (i > maxValue) { + this.updateSkylightNeighborHeight(slice, x, z, maxValue, i + 1); + } else if (i < maxValue) { + this.updateSkylightNeighborHeight(slice, x, z, i, maxValue + 1); + } + + } + + private void updateSkylightNeighborHeight(WorldChunkSlice slice, int x, int z, int startY, int endY) { + if (endY > startY) { + if (!slice.isLoaded(x, z, 16)) { + return; + } + + for(int i = startY; i < endY; ++i) { + this.world.checkLightFor(EnumSkyBlock.SKY, new BlockPos(x, i, z)); + } + + this.dirty = true; + } + + } + + @Shadow + public abstract int getHeightValue(int var1, int var2); + + public short[] getNeighborLightChecks() { + return this.neighborLightChecks; + } + + public void setNeighborLightChecks(short[] data) { + this.neighborLightChecks = data; + } + + public ILightingEngine getLightingEngine() { + return this.lightingEngine; + } + + public boolean isLightInitialized() { + return this.isLightInitialized; + } + + public void setLightInitialized(boolean lightInitialized) { + this.isLightInitialized = lightInitialized; + } + + @Shadow + protected abstract void setSkylightUpdated(); + + public void setSkylightUpdatedPublic() { + this.setSkylightUpdated(); + } + + public int getCachedLightFor(EnumSkyBlock type, BlockPos pos) { + int i = pos.getX() & 15; + int j = pos.getY(); + int k = pos.getZ() & 15; + ExtendedBlockStorage extendedblockstorage = this.storageArrays[j >> 4]; + if (extendedblockstorage == Chunk.NULL_BLOCK_STORAGE) { + return this.canSeeSky(pos) ? type.defaultLightValue : 0; + } else if (type == EnumSkyBlock.SKY) { + return !this.world.provider.hasSkyLight() ? 0 : extendedblockstorage.getSkyLight(i, j & 15, k); + } else { + return type == EnumSkyBlock.BLOCK ? extendedblockstorage.getBlockLight(i, j & 15, k) : type.defaultLightValue; + } + } + + + static { + HORIZONTAL = EnumFacing.Plane.HORIZONTAL.facings(); + } +} diff --git a/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinSPacketChunkData.java b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinSPacketChunkData.java new file mode 100644 index 0000000..9ccd267 --- /dev/null +++ b/src/main/java/org/dimdev/jeid/mixin/modsupport/phosphor/MixinSPacketChunkData.java @@ -0,0 +1,72 @@ +package org.dimdev.jeid.mixin.modsupport.phosphor; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; +import net.minecraft.network.PacketBuffer; +import net.minecraft.network.play.server.SPacketChunkData; +import net.minecraft.world.chunk.Chunk; +import org.dimdev.jeid.ducks.INewChunk; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +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.CallbackInfoReturnable; + +@Mixin(SPacketChunkData.class) +public abstract class MixinSPacketChunkData { + @Shadow + public abstract boolean isFullChunk(); + + /** + * @reason Write the biome int array. + **/ + @Inject(method = "extractChunkData", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/play/server/SPacketChunkData;isFullChunk()Z", ordinal = 1)) + public void reid$writeBiomeArray(PacketBuffer buf, Chunk chunk, boolean writeSkylight, int changedSectionFilter, CallbackInfoReturnable cir) { + if (isFullChunk()) { + buf.writeVarIntArray(((INewChunk) chunk).getIntBiomeArray()); + } + } + + /** + * @reason Disable writing biome byte array. + **/ + @Redirect(method = "extractChunkData", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/play/server/SPacketChunkData;isFullChunk()Z", ordinal = 1)) + public boolean reid$getIsFullChunk(SPacketChunkData packet) { + return false; + } + + /** + * @reason Disable adding biome byte array size. + **/ + @Redirect(method = "calculateChunkSize", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/play/server/SPacketChunkData;isFullChunk()Z", ordinal = 1)) + public boolean reid$getIsFullChunk1(SPacketChunkData packet) { + return false; + } + + @ModifyReturnValue(method = "calculateChunkSize", at = @At(value = "RETURN")) + public int reid$addIntBiomeArraySize(int originalSize, Chunk chunkIn) { + if (isFullChunk()) { + return originalSize + this.getVarIntArraySize(((INewChunk) chunkIn).getIntBiomeArray()); + } + return originalSize; + } + + @Unique + private int getVarIntArraySize(int[] array) { + int size = PacketBuffer.getVarIntSize(array.length); + for (int i : array) { + size += PacketBuffer.getVarIntSize(i); + } + return size; + } + + @Inject( + method = {"calculateChunkSize"}, + at = {@At("HEAD")} + ) + private void onCalculateChunkSize(Chunk chunkIn, boolean hasSkyLight, int changedSectionFilter, CallbackInfoReturnable cir) { + ((ILightingEngineProvider)chunkIn).getLightingEngine().processLightUpdates(); + } +}