From 1cfad31a0d3fb8d8aaac513d9a36e6d8728ec0d5 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Mon, 13 Dec 2021 13:30:43 +0000 Subject: [PATCH] Separate breaking progress for wired modems This means that if the current player is breaking a cable/wired modem, only the part they're looking at has breaking progress. Closes #355. A mixin is definitely not the cleanest way to do this. There's a couple of alternatives: - CodeChickenLib's approach of overriding the BlockRendererDispatcher instance with a delegating subclasss. One mod doing this is fine, several is Not Great.o - Adding a PR to Forge: I started this, and it's definitely the ideal solution, but any event for this would have a ton of fields and just ended up looking super ugly. --- build.gradle | 28 ++++-- .../mixin/BlockRendererDispatcherMixin.java | 92 +++++++++++++++++++ src/main/resources/computercraft.mixins.json | 13 +++ 3 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 src/main/java/dan200/computercraft/mixin/BlockRendererDispatcherMixin.java create mode 100644 src/main/resources/computercraft.mixins.json diff --git a/build.gradle b/build.gradle index d40ae19ce..ded8570c1 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ buildscript { } dependencies { classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+' + classpath "org.spongepowered:mixingradle:0.7.+" classpath 'org.parchmentmc:librarian:1.+' } } @@ -22,6 +23,7 @@ plugins { } apply plugin: 'net.minecraftforge.gradle' +apply plugin: "org.spongepowered.mixin" apply plugin: 'org.parchmentmc.librarian.forgegradle' version = mod_version @@ -64,6 +66,8 @@ minecraft { source sourceSets.main } } + + arg "-mixin.config=computercraft.mixins.json" } client { @@ -109,6 +113,10 @@ minecraft { accessTransformer file('src/testMod/resources/META-INF/accesstransformer.cfg') } +mixin { + add sourceSets.main, 'computercraft.mixins.refmap.json' +} + repositories { mavenCentral() maven { @@ -130,6 +138,7 @@ dependencies { checkstyle "com.puppycrawl.tools:checkstyle:8.25" minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}" + annotationProcessor 'org.spongepowered:mixin:0.8.4:processor' compileOnly fg.deobf("mezz.jei:jei-1.16.5:7.7.0.104:api") compileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.16.5:7.1.0.313") @@ -149,7 +158,7 @@ dependencies { testModImplementation sourceSets.main.output - cctJavadoc 'cc.tweaked:cct-javadoc:1.4.2' + cctJavadoc 'cc.tweaked:cct-javadoc:1.4.4' } // Compile tasks @@ -181,13 +190,16 @@ task luaJavadoc(type: Javadoc) { jar { manifest { - attributes(["Specification-Title" : "computercraft", - "Specification-Vendor" : "SquidDev", - "Specification-Version" : "1", - "Implementation-Title" : "CC: Tweaked", - "Implementation-Version" : "${mod_version}", - "Implementation-Vendor" : "SquidDev", - "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")]) + attributes([ + "Specification-Title" : "computercraft", + "Specification-Vendor" : "SquidDev", + "Specification-Version" : "1", + "Implementation-Title" : "CC: Tweaked", + "Implementation-Version" : "${mod_version}", + "Implementation-Vendor" : "SquidDev", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + "MixinConfigs" : "computercraft.mixins.json", + ]) } from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) } diff --git a/src/main/java/dan200/computercraft/mixin/BlockRendererDispatcherMixin.java b/src/main/java/dan200/computercraft/mixin/BlockRendererDispatcherMixin.java new file mode 100644 index 000000000..1d05c2b59 --- /dev/null +++ b/src/main/java/dan200/computercraft/mixin/BlockRendererDispatcherMixin.java @@ -0,0 +1,92 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.mixin; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.vertex.IVertexBuilder; +import dan200.computercraft.shared.Registry; +import dan200.computercraft.shared.peripheral.modem.wired.BlockCable; +import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant; +import dan200.computercraft.shared.peripheral.modem.wired.CableShapes; +import dan200.computercraft.shared.util.WorldUtil; +import net.minecraft.block.BlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BlockModelRenderer; +import net.minecraft.client.renderer.BlockModelShapes; +import net.minecraft.client.renderer.BlockRendererDispatcher; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockRayTraceResult; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.IBlockDisplayReader; +import net.minecraftforge.client.model.data.IModelData; +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.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Random; + +/** + * Provides custom block breaking progress for modems, so it only applies to the current part. + * + * @see BlockRendererDispatcher#renderBlockDamage(BlockState, BlockPos, IBlockDisplayReader, MatrixStack, IVertexBuilder, IModelData) + */ +@Mixin( BlockRendererDispatcher.class ) +public class BlockRendererDispatcherMixin +{ + @Shadow + private final Random random; + @Shadow + private final BlockModelShapes blockModelShaper; + @Shadow + private final BlockModelRenderer modelRenderer; + + public BlockRendererDispatcherMixin( Random random, BlockModelShapes blockModelShaper, BlockModelRenderer modelRenderer ) + { + this.random = random; + this.blockModelShaper = blockModelShaper; + this.modelRenderer = modelRenderer; + } + + @Inject( + method = "name=/^renderBlockDamage$/ desc=/IModelData;\\)V$/", + at = @At( "HEAD" ), + cancellable = true, + require = 0 // This isn't critical functionality, so don't worry if we can't apply it. + ) + public void renderBlockDamage( + BlockState state, BlockPos pos, IBlockDisplayReader world, MatrixStack pose, IVertexBuilder buffers, IModelData modelData, + CallbackInfo info + ) + { + // Only apply to cables which have both a cable and modem + if( state.getBlock() != Registry.ModBlocks.CABLE.get() + || !state.getValue( BlockCable.CABLE ) + || state.getValue( BlockCable.MODEM ) == CableModemVariant.None + ) + { + return; + } + + RayTraceResult hit = Minecraft.getInstance().hitResult; + if( hit == null || hit.getType() != RayTraceResult.Type.BLOCK ) return; + BlockPos hitPos = ((BlockRayTraceResult) hit).getBlockPos(); + + if( !hitPos.equals( pos ) ) return; + + info.cancel(); + BlockState newState = WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.getLocation().subtract( pos.getX(), pos.getY(), pos.getZ() ) ) + ? state.getBlock().defaultBlockState().setValue( BlockCable.MODEM, state.getValue( BlockCable.MODEM ) ) + : state.setValue( BlockCable.MODEM, CableModemVariant.None ); + + IBakedModel model = blockModelShaper.getBlockModel( newState ); + long seed = newState.getSeed( pos ); + modelRenderer.renderModel( world, model, newState, pos, pose, buffers, true, random, seed, OverlayTexture.NO_OVERLAY, modelData ); + } +} diff --git a/src/main/resources/computercraft.mixins.json b/src/main/resources/computercraft.mixins.json new file mode 100644 index 000000000..9da186e3b --- /dev/null +++ b/src/main/resources/computercraft.mixins.json @@ -0,0 +1,13 @@ +{ + "minVersion": "0.8", + "required": true, + "compatibilityLevel": "JAVA_8", + "refmap": "computercraft.mixins.refmap.json", + "package": "dan200.computercraft.mixin", + "client": [ + "BlockRendererDispatcherMixin" + ], + "injectors": { + "defaultRequire": 1 + } +}