Add event for preventing particle tinting for colored blocks ()

- Adds an event, `ParticleRenderEvents.ALLOW_BLOCK_DUST_TINT`, that checks
  if a block dust particle of a specific block can be tinted.
- Bumps Fabric Loader requirement of fabric-particles-v1 to latest to
  disable Loader's mixin compatibility mode that ignores slices for
  certain At annotations.

Signed-off-by: modmuss50 <modmuss50@gmail.com>
This commit is contained in:
Juuz 2023-07-03 15:11:13 +03:00 committed by modmuss50
parent c956c0e61d
commit 86d48884d3
18 changed files with 354 additions and 1 deletions
fabric-particles-v1
build.gradle
src
client
java/net/fabricmc/fabric
api/client/particle/v1
mixin/client/particle
resources
main/resources
testmod
testmodClient/java/net/fabricmc/fabric/test/particle/client

View file

@ -7,6 +7,12 @@ loom {
moduleDependencies(project, ['fabric-api-base'])
testDependencies(project, [
':fabric-command-api-v2',
':fabric-rendering-v1',
':fabric-resource-loader-v0'
])
validateMixinNames {
// Loom needs to handle inner mixins better
exclude "**/ParticleManagerAccessor\$SimpleSpriteProviderAccessor.class"

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.client.particle.v1;
import net.minecraft.block.BlockState;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.BlockPos;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
/**
* Events related to particle rendering.
*/
public final class ParticleRenderEvents {
private ParticleRenderEvents() {
}
/**
* An event that checks if a {@linkplain net.minecraft.client.particle.BlockDustParticle block dust particle}
* can be tinted using the corresponding block's {@linkplain net.minecraft.client.color.block.BlockColorProvider color provider}.
*
* <p>The default return value of this event is {@code true}. If any callback returns {@code false} for a given call,
* further iteration will be canceled and the event invoker will return {@code false}.
*/
public static final Event<AllowBlockDustTint> ALLOW_BLOCK_DUST_TINT = EventFactory.createArrayBacked(AllowBlockDustTint.class, callbacks -> (state, world, pos) -> {
for (AllowBlockDustTint callback : callbacks) {
if (!callback.allowBlockDustTint(state, world, pos)) {
return false;
}
}
return true;
});
@FunctionalInterface
public interface AllowBlockDustTint {
/**
* Checks whether a {@linkplain net.minecraft.client.particle.BlockDustParticle block dust particle} can be
* tinted using the corresponding block's {@linkplain net.minecraft.client.color.block.BlockColorProvider color provider}.
*
* @param state the block state that the particle represents
* @param world the world the particle is created in
* @param pos the position of the particle
* @return {@code true} if color provider tinting should be allowed, {@code false} otherwise
*/
boolean allowBlockDustTint(BlockState state, ClientWorld world, BlockPos pos);
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.client.particle;
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.Slice;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.particle.BlockDustParticle;
import net.minecraft.client.particle.SpriteBillboardParticle;
import net.minecraft.util.math.BlockPos;
import net.fabricmc.fabric.api.client.particle.v1.ParticleRenderEvents;
// Implements ParticleRenderEvents.ALLOW_BLOCK_DUST_TINT
@Mixin(BlockDustParticle.class)
abstract class BlockDustParticleMixin extends SpriteBillboardParticle {
@Shadow
@Final
private BlockPos blockPos;
private BlockDustParticleMixin() {
super(null, 0, 0, 0);
}
@ModifyVariable(
method = "<init>(Lnet/minecraft/client/world/ClientWorld;DDDDDDLnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;)V",
at = @At("LOAD"),
argsOnly = true,
slice = @Slice(
from = @At(value = "FIELD", target = "Lnet/minecraft/client/particle/BlockDustParticle;blue:F", ordinal = 0),
to = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isOf(Lnet/minecraft/block/Block;)Z")
),
allow = 1
)
private BlockState removeUntintableParticles(BlockState state) {
if (!ParticleRenderEvents.ALLOW_BLOCK_DUST_TINT.invoker().allowBlockDustTint(state, world, blockPos)) {
// As of 1.20.1, vanilla hardcodes grass block particles to not get tinted.
return Blocks.GRASS_BLOCK.getDefaultState();
}
return state;
}
}

View file

@ -3,6 +3,7 @@
"package": "net.fabricmc.fabric.mixin.client.particle",
"compatibilityLevel": "JAVA_16",
"client": [
"BlockDustParticleMixin",
"ParticleManagerMixin",
"ParticleManagerAccessor",
"ParticleManagerAccessor$SimpleSpriteProviderAccessor"

View file

@ -16,7 +16,7 @@
"FabricMC"
],
"depends": {
"fabricloader": ">=0.4.0"
"fabricloader": ">=0.14.21"
},
"description": "Hooks for registering custom particles.",
"mixins": [

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.particle;
import com.mojang.brigadier.Command;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.Material;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.server.command.CommandManager;
import net.minecraft.util.Identifier;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
public final class ParticleTestSetup implements ModInitializer {
// The dust particles of this block are always tinted (default).
public static final Block ALWAYS_TINTED = new ParticleTintTestBlock(AbstractBlock.Settings.of(Material.STONE).breakInstantly(), 0xFF00FF);
// The dust particles of this block are only tinted when the block is broken over water.
public static final Block TINTED_OVER_WATER = new ParticleTintTestBlock(AbstractBlock.Settings.of(Material.STONE).breakInstantly(), 0xFFFF00);
// The dust particles of this block are never tinted.
public static final Block NEVER_TINTED = new ParticleTintTestBlock(AbstractBlock.Settings.of(Material.STONE).breakInstantly(), 0x00FFFF);
@Override
public void onInitialize() {
registerBlock("always_tinted", ALWAYS_TINTED);
registerBlock("tinted_over_water", TINTED_OVER_WATER);
registerBlock("never_tinted", NEVER_TINTED);
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("addparticletestblocks").executes(context -> {
PlayerInventory inventory = context.getSource().getPlayer().getInventory();
inventory.offerOrDrop(new ItemStack(ALWAYS_TINTED));
inventory.offerOrDrop(new ItemStack(TINTED_OVER_WATER));
inventory.offerOrDrop(new ItemStack(NEVER_TINTED));
return Command.SINGLE_SUCCESS;
}));
});
}
private static void registerBlock(String path, Block block) {
Identifier id = new Identifier("fabric-particles-v1-testmod", path);
Registry.register(Registries.BLOCK, id, block);
Registry.register(Registries.ITEM, id, new BlockItem(block, new Item.Settings()));
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.particle;
import net.minecraft.block.Block;
public class ParticleTintTestBlock extends Block {
public final int color;
public ParticleTintTestBlock(Settings settings, int color) {
super(settings);
this.color = color;
}
}

View file

@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "fabric-particles-v1-testmod:block/tint_test" }
}
}

View file

@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "fabric-particles-v1-testmod:block/tint_test" }
}
}

View file

@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "fabric-particles-v1-testmod:block/tint_test" }
}
}

View file

@ -0,0 +1,5 @@
{
"block.fabric-particles-v1-testmod.always_tinted": "Dust Particles Always Tinted",
"block.fabric-particles-v1-testmod.never_tinted": "Dust Particles Never Tinted",
"block.fabric-particles-v1-testmod.tinted_over_water": "Dust Particles Only Tinted Over Water"
}

View file

@ -0,0 +1,21 @@
{
"parent": "block/block",
"textures": {
"all": "fabric-particles-v1-testmod:block/tint_test",
"particle": "fabric-particles-v1-testmod:block/tint_test"
},
"elements": [
{
"from": [0, 0, 0],
"to": [16, 16, 16],
"faces": {
"north": { "texture": "#all", "cullface": "north", "tintindex": 0 },
"east": { "texture": "#all", "cullface": "east", "tintindex": 0 },
"south": { "texture": "#all", "cullface": "south", "tintindex": 0 },
"west": { "texture": "#all", "cullface": "west", "tintindex": 0 },
"up": { "texture": "#all", "cullface": "up", "tintindex": 0 },
"down": { "texture": "#all", "cullface": "down", "tintindex": 0 }
}
}
]
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "fabric-particles-v1-testmod:block/tint_test"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "fabric-particles-v1-testmod:block/tint_test"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "fabric-particles-v1-testmod:block/tint_test"
}
}

View file

@ -0,0 +1,19 @@
{
"schemaVersion": 1,
"id": "fabric-particles-v1-testmod",
"name": "Fabric Particles (v1) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-particles-v1": "*"
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.particle.ParticleTestSetup"
],
"client": [
"net.fabricmc.fabric.test.particle.client.ParticleRenderEventTests"
]
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.particle.client;
import net.minecraft.registry.tag.FluidTags;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.particle.v1.ParticleRenderEvents;
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
import net.fabricmc.fabric.test.particle.ParticleTestSetup;
import net.fabricmc.fabric.test.particle.ParticleTintTestBlock;
public final class ParticleRenderEventTests implements ClientModInitializer {
@Override
public void onInitializeClient() {
ColorProviderRegistry.BLOCK.register((state, world, pos, tintIndex) -> {
if (tintIndex == 0) {
return ((ParticleTintTestBlock) state.getBlock()).color;
}
return -1;
}, ParticleTestSetup.ALWAYS_TINTED, ParticleTestSetup.TINTED_OVER_WATER, ParticleTestSetup.NEVER_TINTED);
ParticleRenderEvents.ALLOW_BLOCK_DUST_TINT.register((state, world, pos) -> {
if (state.isOf(ParticleTestSetup.NEVER_TINTED)) {
return false;
} else if (state.isOf(ParticleTestSetup.TINTED_OVER_WATER)) {
return world.getFluidState(pos.down()).isIn(FluidTags.WATER);
}
return true;
});
}
}