diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/FabricParticleManager.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/FabricParticleManager.java deleted file mode 100644 index bad4dadf1..000000000 --- a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/FabricParticleManager.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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.impl.client.particle; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Random; -import java.util.stream.Collectors; - -import com.google.common.collect.ImmutableList; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - -import net.minecraft.client.particle.ParticleTextureData; -import net.minecraft.client.texture.Sprite; -import net.minecraft.client.texture.SpriteAtlasTexture; -import net.minecraft.resource.ResourceManager; -import net.minecraft.util.Identifier; -import net.minecraft.util.JsonHelper; - -import net.fabricmc.fabric.api.client.particle.v1.FabricSpriteProvider; - -public final class FabricParticleManager { - private final VanillaParticleManager manager; - - private final Int2ObjectMap providers = new Int2ObjectOpenHashMap<>(); - - public FabricParticleManager(VanillaParticleManager manager) { - this.manager = manager; - } - - public void injectValues() { - manager.getFactories().putAll(ParticleFactoryRegistryImpl.INSTANCE.factories); - ParticleFactoryRegistryImpl.INSTANCE.constructors.forEach((id, factory) -> { - FabricSpriteProviderImpl provider = new FabricSpriteProviderImpl(); - - providers.put((int) id, provider); - manager.getFactories().put((int) id, factory.create(provider)); - }); - } - - private FabricSpriteProviderImpl getProvider(Identifier id) { - if (!ParticleFactoryRegistryImpl.INSTANCE.constructorsIdsMap.containsKey(id)) { - return null; - } - - return providers.get((int) ParticleFactoryRegistryImpl.INSTANCE.constructorsIdsMap.get(id)); - } - - public boolean loadParticle(ResourceManager manager, Identifier id) { - FabricSpriteProviderImpl provider = getProvider(id); - - if (provider == null) { - return false; // preserve vanilla behaviour (i don't got dis) - } - - Identifier file = new Identifier(id.getNamespace(), "particles/" + id.getPath() + ".json"); - - try (Reader reader = new InputStreamReader(manager.getResource(file).getInputStream(), StandardCharsets.UTF_8)) { - List spriteIds = ParticleTextureData.load(JsonHelper.deserialize(reader)).getTextureList(); - - if (spriteIds == null) { - // Particles should have a list of picks, even if it's just empty. - throw new IllegalStateException("(Fabric) Missing texture list for particle " + id); - } - - provider.setSprites(spriteIds); - } catch (IOException e) { - throw new IllegalStateException("Failed to load description for particle " + id, e); - } - - return true; // i got dis - } - - private final class FabricSpriteProviderImpl implements FabricSpriteProvider { - private List spriteIds; - - // @Nullable - private List sprites; - - @Override - public Sprite getSprite(int min, int max) { - return getSprites().get(min * (getSprites().size() - 1) / max); - } - - @Override - public Sprite getSprite(Random random_1) { - return getSprites().get(random_1.nextInt(getSprites().size())); - } - - @Override - public SpriteAtlasTexture getAtlas() { - return manager.getAtlas(); - } - - @Override - public List getSprites() { - if (sprites == null) { - sprites = spriteIds.stream().map(getAtlas()::getSprite).collect(Collectors.toList()); - } - - return sprites; - } - - public void setSprites(List sprites) { - this.sprites = null; - this.spriteIds = ImmutableList.copyOf(sprites); - } - } -} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/FabricSpriteProviderImpl.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/FabricSpriteProviderImpl.java new file mode 100644 index 000000000..a2b12a2a0 --- /dev/null +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/FabricSpriteProviderImpl.java @@ -0,0 +1,58 @@ +/* + * 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.impl.client.particle; + +import java.util.List; +import java.util.Random; + +import net.minecraft.client.particle.ParticleManager; +import net.minecraft.client.particle.SpriteProvider; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.texture.SpriteAtlasTexture; + +import net.fabricmc.fabric.api.client.particle.v1.FabricSpriteProvider; +import net.fabricmc.fabric.mixin.client.particle.ParticleManagerAccessor; + +public class FabricSpriteProviderImpl implements FabricSpriteProvider { + private final ParticleManager particleManager; + private final SpriteProvider delegate; + + FabricSpriteProviderImpl(ParticleManager particleManager, SpriteProvider delegate) { + this.particleManager = particleManager; + this.delegate = delegate; + } + + @Override + public SpriteAtlasTexture getAtlas() { + return ((ParticleManagerAccessor) particleManager).getParticleAtlasTexture(); + } + + @Override + public List getSprites() { + return ((ParticleManagerAccessor.SimpleSpriteProviderAccessor) delegate).getSprites(); + } + + @Override + public Sprite getSprite(int i, int j) { + return delegate.getSprite(i, j); + } + + @Override + public Sprite getSprite(Random random) { + return delegate.getSprite(random); + } +} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/ParticleFactoryRegistryImpl.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/ParticleFactoryRegistryImpl.java index eabc58512..285eb4b43 100644 --- a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/ParticleFactoryRegistryImpl.java +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/ParticleFactoryRegistryImpl.java @@ -16,37 +16,116 @@ package net.fabricmc.fabric.impl.client.particle; -import java.util.HashMap; +import java.lang.reflect.Constructor; +import java.util.IdentityHashMap; import java.util.Map; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - import net.minecraft.client.particle.ParticleFactory; +import net.minecraft.client.particle.ParticleManager; +import net.minecraft.client.particle.SpriteProvider; import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleType; -import net.minecraft.util.Identifier; import net.minecraft.util.registry.Registry; +import net.fabricmc.fabric.api.client.particle.v1.FabricSpriteProvider; +import net.fabricmc.fabric.mixin.client.particle.ParticleManagerAccessor; +import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry; public final class ParticleFactoryRegistryImpl implements ParticleFactoryRegistry { public static final ParticleFactoryRegistryImpl INSTANCE = new ParticleFactoryRegistryImpl(); - final Int2ObjectMap> factories = new Int2ObjectOpenHashMap<>(); - final Int2ObjectMap> constructors = new Int2ObjectOpenHashMap<>(); - final Map constructorsIdsMap = new HashMap<>(); + static class DeferredParticleFactoryRegistry implements ParticleFactoryRegistry { + private final Map, ParticleFactory> factories = new IdentityHashMap<>(); + private final Map, PendingParticleFactory> constructors = new IdentityHashMap<>(); + + @Override + public void register(ParticleType type, ParticleFactory factory) { + factories.put(type, factory); + } + + @Override + public void register(ParticleType type, PendingParticleFactory factory) { + constructors.put(type, factory); + } + + @SuppressWarnings("unchecked") + void applyTo(ParticleFactoryRegistry registry) { + for (Map.Entry, ParticleFactory> entry : factories.entrySet()) { + ParticleType type = entry.getKey(); + ParticleFactory factory = entry.getValue(); + registry.register(type, factory); + } + + for (Map.Entry, PendingParticleFactory> entry : constructors.entrySet()) { + ParticleType type = entry.getKey(); + PendingParticleFactory constructor = entry.getValue(); + registry.register(type, constructor); + } + } + } + + static class DirectParticleFactoryRegistry implements ParticleFactoryRegistry { + private static final Constructor SIMPLE_SPRITE_PROVIDER_CONSTRUCTOR; + static { + try { + String intermediaryClassName = "net.minecraft.class_702$class_4090"; + String currentClassName = FabricLoader.getInstance().getMappingResolver().mapClassName("intermediary", intermediaryClassName); + @SuppressWarnings("unchecked") + Class clazz = (Class) Class.forName(currentClassName); + SIMPLE_SPRITE_PROVIDER_CONSTRUCTOR = clazz.getDeclaredConstructor(ParticleManager.class); + SIMPLE_SPRITE_PROVIDER_CONSTRUCTOR.setAccessible(true); + } catch (Exception e) { + throw new RuntimeException("Unable to register particles", e); + } + } + + private static SpriteProvider createSimpleSpriteProvider(ParticleManager particleManager) { + try { + return SIMPLE_SPRITE_PROVIDER_CONSTRUCTOR.newInstance(particleManager); + } catch (Exception e) { + throw new RuntimeException("Unable to create SimpleSpriteProvider", e); + } + } + + private final ParticleManager particleManager; + + DirectParticleFactoryRegistry(ParticleManager particleManager) { + this.particleManager = particleManager; + } + + @Override + public void register(ParticleType type, ParticleFactory factory) { + ((ParticleManagerAccessor) particleManager).getFactories().put(Registry.PARTICLE_TYPE.getRawId(type), factory); + } + + @Override + public void register(ParticleType type, PendingParticleFactory constructor) { + SpriteProvider delegate = createSimpleSpriteProvider(particleManager); + FabricSpriteProvider fabricSpriteProvider = new FabricSpriteProviderImpl(particleManager, delegate); + ((ParticleManagerAccessor) particleManager).getSpriteAwareFactories().put(Registry.PARTICLE_TYPE.getId(type), delegate); + register(type, constructor.create(fabricSpriteProvider)); + } + } + + ParticleFactoryRegistry internalRegistry = new DeferredParticleFactoryRegistry(); private ParticleFactoryRegistryImpl() { } @Override public void register(ParticleType type, ParticleFactory factory) { - factories.put(Registry.PARTICLE_TYPE.getRawId(type), factory); + internalRegistry.register(type, factory); } @Override - public void register(ParticleType type, PendingParticleFactory factory) { - constructors.put(Registry.PARTICLE_TYPE.getRawId(type), factory); - constructorsIdsMap.put(Registry.PARTICLE_TYPE.getId(type), Registry.PARTICLE_TYPE.getRawId(type)); + public void register(ParticleType type, PendingParticleFactory constructor) { + internalRegistry.register(type, constructor); + } + + public void initialize(ParticleManager particleManager) { + ParticleFactoryRegistry newRegistry = new DirectParticleFactoryRegistry(particleManager); + DeferredParticleFactoryRegistry oldRegistry = (DeferredParticleFactoryRegistry) internalRegistry; + oldRegistry.applyTo(newRegistry); + internalRegistry = newRegistry; } } diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/VanillaParticleManager.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/VanillaParticleManager.java deleted file mode 100644 index 174413d27..000000000 --- a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/VanillaParticleManager.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.impl.client.particle; - -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; - -import net.minecraft.client.particle.ParticleFactory; -import net.minecraft.client.texture.SpriteAtlasTexture; - -/** - * Accessors for some private members of ParticleManager. - * - *

Usage: - *

 {@code
- * SpriteAtlasTexture atlas = ((VanillaParticleManager)MinecraftClient.getInstance().particleManager).getAtlas()
- * }
- */ -public interface VanillaParticleManager { - SpriteAtlasTexture getAtlas(); - - Int2ObjectMap> getFactories(); -} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/MixinParticleManager.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/MixinParticleManager.java index 9e063e983..dbd0f24e0 100644 --- a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/MixinParticleManager.java +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/MixinParticleManager.java @@ -16,48 +16,19 @@ package net.fabricmc.fabric.mixin.client.particle; -import java.util.List; -import java.util.Map; - -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import net.minecraft.client.particle.ParticleFactory; import net.minecraft.client.particle.ParticleManager; -import net.minecraft.client.texture.SpriteAtlasTexture; -import net.minecraft.resource.ResourceManager; -import net.minecraft.util.Identifier; -import net.fabricmc.fabric.impl.client.particle.FabricParticleManager; -import net.fabricmc.fabric.impl.client.particle.VanillaParticleManager; +import net.fabricmc.fabric.impl.client.particle.ParticleFactoryRegistryImpl; @Mixin(ParticleManager.class) -public abstract class MixinParticleManager implements VanillaParticleManager { - private final FabricParticleManager fabricParticleManager = new FabricParticleManager(this); - - @Override - @Accessor("particleAtlasTexture") - public abstract SpriteAtlasTexture getAtlas(); - - @Override - @Accessor("factories") - public abstract Int2ObjectMap> getFactories(); - +public abstract class MixinParticleManager { @Inject(method = "registerDefaultFactories()V", at = @At("RETURN")) private void onRegisterDefaultFactories(CallbackInfo info) { - fabricParticleManager.injectValues(); - } - - @Inject(method = "loadTextureList(Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/util/Identifier;Ljava/util/Map;)V", - at = @At("HEAD"), - cancellable = true) - private void onLoadTextureList(ResourceManager manager, Identifier id, Map> output, CallbackInfo info) { - if (fabricParticleManager.loadParticle(manager, id)) { - info.cancel(); - } + ParticleFactoryRegistryImpl.INSTANCE.initialize((ParticleManager) (Object) this); } } diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/ParticleManagerAccessor.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/ParticleManagerAccessor.java new file mode 100644 index 000000000..915daff71 --- /dev/null +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/ParticleManagerAccessor.java @@ -0,0 +1,35 @@ +package net.fabricmc.fabric.mixin.client.particle; + +import java.util.List; +import java.util.Map; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.particle.ParticleFactory; +import net.minecraft.client.particle.ParticleManager; +import net.minecraft.client.particle.SpriteProvider; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.util.Identifier; + +@Mixin(ParticleManager.class) +public interface ParticleManagerAccessor { + @Accessor("particleAtlasTexture") + SpriteAtlasTexture getParticleAtlasTexture(); + + @Accessor("factories") + Int2ObjectMap> getFactories(); + + // NOTE: The field signature is actually Map + // This still works due to type erasure + @Accessor("spriteAwareFactories") + Map getSpriteAwareFactories(); + + @Mixin(targets = "net/minecraft/client/particle/ParticleManager$SimpleSpriteProvider") + interface SimpleSpriteProviderAccessor { + @Accessor("sprites") + List getSprites(); + } +} diff --git a/fabric-particles-v1/src/main/resources/fabric-particles-v1.mixins.json b/fabric-particles-v1/src/main/resources/fabric-particles-v1.mixins.json index c12763163..7ab8dd098 100644 --- a/fabric-particles-v1/src/main/resources/fabric-particles-v1.mixins.json +++ b/fabric-particles-v1/src/main/resources/fabric-particles-v1.mixins.json @@ -3,7 +3,9 @@ "package": "net.fabricmc.fabric.mixin.client.particle", "compatibilityLevel": "JAVA_8", "client": [ - "MixinParticleManager" + "MixinParticleManager", + "ParticleManagerAccessor", + "ParticleManagerAccessor$SimpleSpriteProviderAccessor" ], "injectors": { "defaultRequire": 1