diff --git a/common/src/main/java/dev/architectury/registry/client/particle/ParticleProviderRegistry.java b/common/src/main/java/dev/architectury/registry/client/particle/ParticleProviderRegistry.java new file mode 100644 index 00000000..1dde2190 --- /dev/null +++ b/common/src/main/java/dev/architectury/registry/client/particle/ParticleProviderRegistry.java @@ -0,0 +1,56 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.particle; + +import dev.architectury.injectables.annotations.ExpectPlatform; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.client.particle.SpriteSet; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleType; + +import java.util.List; + +@Environment(EnvType.CLIENT) +public final class ParticleProviderRegistry { + public interface ExtendedSpriteSet extends SpriteSet { + TextureAtlas getAtlas(); + + List getSprites(); + } + + @ExpectPlatform + public static void register(ParticleType type, ParticleProvider provider) { + throw new AssertionError(); + } + + @ExpectPlatform + public static void register(ParticleType type, DeferredParticleProvider provider) { + throw new AssertionError(); + } + + @FunctionalInterface + public interface DeferredParticleProvider { + ParticleProvider create(ExtendedSpriteSet spriteSet); + } +} diff --git a/fabric/src/main/java/dev/architectury/registry/client/particle/fabric/ParticleProviderRegistryImpl.java b/fabric/src/main/java/dev/architectury/registry/client/particle/fabric/ParticleProviderRegistryImpl.java new file mode 100644 index 00000000..7c67f80b --- /dev/null +++ b/fabric/src/main/java/dev/architectury/registry/client/particle/fabric/ParticleProviderRegistryImpl.java @@ -0,0 +1,67 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.particle.fabric; + +import dev.architectury.registry.client.particle.ParticleProviderRegistry; +import net.fabricmc.fabric.api.client.particle.v1.FabricSpriteProvider; +import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleType; + +import java.util.List; +import java.util.Random; + +public class ParticleProviderRegistryImpl { + public record ExtendedSpriteSetImpl( + FabricSpriteProvider delegate + ) implements ParticleProviderRegistry.ExtendedSpriteSet { + @Override + public TextureAtlas getAtlas() { + return delegate.getAtlas(); + } + + @Override + public List getSprites() { + return delegate.getSprites(); + } + + @Override + public TextureAtlasSprite get(int i, int j) { + return delegate.get(i, j); + } + + @Override + public TextureAtlasSprite get(Random random) { + return delegate.get(random); + } + } + + public static void register(ParticleType type, ParticleProvider provider) { + ParticleFactoryRegistry.getInstance().register(type, provider); + } + + public static void register(ParticleType type, ParticleProviderRegistry.DeferredParticleProvider provider) { + ParticleFactoryRegistry.getInstance().register(type, sprites -> + provider.create(new ExtendedSpriteSetImpl(sprites))); + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/mixin/forge/ParticleEngineAccessor.java b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/ParticleEngineAccessor.java new file mode 100644 index 00000000..ebee0c3e --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/ParticleEngineAccessor.java @@ -0,0 +1,40 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.mixin.forge; + +import net.minecraft.client.particle.ParticleEngine; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(ParticleEngine.class) +public interface ParticleEngineAccessor { + @Accessor + TextureAtlas getTextureAtlas(); + + @Mixin(targets = "net/minecraft/client/particle/ParticleEngine$MutableSpriteSet") + interface MutableSpriteSetAccessor { + @Accessor + List getSprites(); + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ParticleProviderRegistryImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ParticleProviderRegistryImpl.java new file mode 100644 index 00000000..a520e199 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ParticleProviderRegistryImpl.java @@ -0,0 +1,112 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.registry.forge; + +import me.shedaniel.architectury.forge.ArchitecturyForge; +import me.shedaniel.architectury.mixin.forge.ParticleEngineAccessor; +import me.shedaniel.architectury.registry.ParticleProviderRegistry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.ParticleEngine; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.client.particle.SpriteSet; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleType; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.ParticleFactoryRegisterEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +@Mod.EventBusSubscriber(modid = ArchitecturyForge.MOD_ID, value = Dist.CLIENT) +public class ParticleProviderRegistryImpl { + private static final class ExtendedSpriteSetImpl implements ParticleProviderRegistry.ExtendedSpriteSet { + private final ParticleEngine engine; + private final SpriteSet delegate; + + private ExtendedSpriteSetImpl(ParticleEngine engine, SpriteSet delegate) { + this.engine = engine; + this.delegate = delegate; + } + + @Override + public TextureAtlas getAtlas() { + return ((ParticleEngineAccessor) engine).getTextureAtlas(); + } + + @Override + public List getSprites() { + return ((ParticleEngineAccessor.MutableSpriteSetAccessor) delegate).getSprites(); + } + + @Override + public TextureAtlasSprite get(int i, int j) { + return delegate.get(i, j); + } + + @Override + public TextureAtlasSprite get(Random random) { + return delegate.get(random); + } + } + + private static ArrayList deferred = new ArrayList<>(); + + private static void _register(ParticleType type, ParticleProvider provider) { + Minecraft.getInstance().particleEngine.register(type, provider); + } + + private static void _register(ParticleType type, ParticleProviderRegistry.DeferredParticleProvider provider) { + Minecraft.getInstance().particleEngine.register(type, sprites -> + provider.create(new ExtendedSpriteSetImpl(Minecraft.getInstance().particleEngine, sprites))); + } + + public static void register(ParticleType type, ParticleProvider provider) { + if (deferred == null) { + _register(type, provider); + } else { + deferred.add(() -> _register(type, provider)); + } + } + + public static void register(ParticleType type, ParticleProviderRegistry.DeferredParticleProvider provider) { + if (deferred == null) { + _register(type, provider); + } else { + deferred.add(() -> _register(type, provider)); + } + } + + @SubscribeEvent + public static void onParticleFactoryRegister(ParticleFactoryRegisterEvent unused) { + if (deferred != null) { + // run all deferred registrations + for (Runnable runnable : deferred) { + runnable.run(); + } + // yeet deferred list - register immediately from now on + deferred = null; + } + } +} diff --git a/forge/src/main/resources/architectury.mixins.json b/forge/src/main/resources/architectury.mixins.json index ca07d669..6077bc56 100644 --- a/forge/src/main/resources/architectury.mixins.json +++ b/forge/src/main/resources/architectury.mixins.json @@ -5,7 +5,9 @@ "compatibilityLevel": "JAVA_16", "minVersion": "0.8", "client": [ - "MixinClientLevel" + "MixinClientLevel", + "ParticleEngineAccessor", + "ParticleEngineAccessor$MutableSpriteSetAccessor" ], "mixins": [ "BiomeGenerationSettingsBuilderAccessor", diff --git a/testmod-common/src/main/java/dev/architectury/test/TestMod.java b/testmod-common/src/main/java/dev/architectury/test/TestMod.java index 91ab5f2b..0f94d0c1 100644 --- a/testmod-common/src/main/java/dev/architectury/test/TestMod.java +++ b/testmod-common/src/main/java/dev/architectury/test/TestMod.java @@ -27,6 +27,7 @@ import dev.architectury.test.debug.client.ClientOverlayMessageSink; import dev.architectury.test.entity.TestEntity; import dev.architectury.test.events.DebugEvents; import dev.architectury.test.gamerule.TestGameRules; +import dev.architectury.test.particle.TestParticles; import dev.architectury.test.registry.TestRegistries; import dev.architectury.test.registry.client.TestKeybinds; import dev.architectury.test.tags.TestTags; @@ -48,6 +49,7 @@ public class TestMod { TestGameRules.init(); TestTags.initialize(); TestTrades.init(); + TestParticles.initialize(); if (Platform.getEnvironment() == Env.CLIENT) { initializeClient(); } diff --git a/testmod-common/src/main/java/dev/architectury/test/particle/TestParticles.java b/testmod-common/src/main/java/dev/architectury/test/particle/TestParticles.java new file mode 100644 index 00000000..6529550e --- /dev/null +++ b/testmod-common/src/main/java/dev/architectury/test/particle/TestParticles.java @@ -0,0 +1,44 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.test.particle; + +import dev.architectury.platform.Platform; +import dev.architectury.registry.client.particle.ParticleProviderRegistry; +import dev.architectury.registry.registries.DeferredRegister; +import dev.architectury.registry.registries.RegistrySupplier; +import dev.architectury.test.TestMod; +import dev.architectury.utils.Env; +import net.minecraft.client.particle.HeartParticle; +import net.minecraft.core.Registry; +import net.minecraft.core.particles.ParticleType; +import net.minecraft.core.particles.SimpleParticleType; + +public class TestParticles { + public static final DeferredRegister> PARTICLE_TYPES = DeferredRegister.create(TestMod.MOD_ID, Registry.PARTICLE_TYPE_REGISTRY); + + public static final RegistrySupplier TEST_PARTICLE = PARTICLE_TYPES.register("test_particle", () -> + new SimpleParticleType(false) {}); + + public static void initialize() { + PARTICLE_TYPES.register(); + if (Platform.getEnvironment() == Env.CLIENT) + ParticleProviderRegistry.register(TEST_PARTICLE.get(), HeartParticle.Provider::new); + } +} diff --git a/testmod-common/src/main/resources/assets/architectury-test/particles/test_particle.json b/testmod-common/src/main/resources/assets/architectury-test/particles/test_particle.json new file mode 100644 index 00000000..3743d66d --- /dev/null +++ b/testmod-common/src/main/resources/assets/architectury-test/particles/test_particle.json @@ -0,0 +1,5 @@ +{ + "textures": [ + "architectury-test:test_particle" + ] +} \ No newline at end of file diff --git a/testmod-common/src/main/resources/assets/architectury-test/textures/particle/test_particle.png b/testmod-common/src/main/resources/assets/architectury-test/textures/particle/test_particle.png new file mode 100644 index 00000000..1acc090c Binary files /dev/null and b/testmod-common/src/main/resources/assets/architectury-test/textures/particle/test_particle.png differ