From 8eb0053e04bbbd10761e02b6bcc85086f2e4f3a3 Mon Sep 17 00:00:00 2001 From: westernat Date: Sat, 27 Dec 2025 21:54:03 +0800 Subject: [PATCH 01/13] 1.1.4.2 --- build.gradle | 6 ------ gradle.properties | 2 +- .../api/geckolib/GeckoLibHelper.java | 3 ++- .../api/geckolib/GeoWithCurrentEntity.java | 3 ++- .../data/component/EmitterShape.java | 8 ++++++-- .../component/ParticleAppearanceLighting.java | 2 ++ .../data/component/ParticleMotionCollision.java | 15 ++++++++++----- .../data/component/ParticleMotionDynamic.java | 8 ++++++-- .../particlestorm/data/event/EventRandomize.java | 1 + .../particlestorm/data/molang/BoolMolangExp.java | 3 +-- .../data/molang/FloatMolangExpList.java | 16 ++-------------- .../particle/MolangParticleInstance.java | 4 ++-- .../particle/MolangParticleLoader.java | 2 +- src/main/resources/particlestorm.mixins.json | 7 +++---- 14 files changed, 39 insertions(+), 41 deletions(-) diff --git a/build.gradle b/build.gradle index f35e7fe..4cd5b76 100644 --- a/build.gradle +++ b/build.gradle @@ -46,12 +46,6 @@ neoForge { systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id } - data { - data() - - programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() - } - configureEach { systemProperty 'forge.logging.markers', 'REGISTRIES' systemProperty 'particlestorm.debug', 'true' diff --git a/gradle.properties b/gradle.properties index 770f008..83a3567 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.1.4 +mod_version=1.1.4.2 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java index f42d1a8..18206d8 100644 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java @@ -1,5 +1,6 @@ package org.mesdag.particlestorm.api.geckolib; +import com.mojang.datafixers.DSL; import it.unimi.dsi.fastutil.ints.IntIterator; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; @@ -37,7 +38,7 @@ public static void registerStuffs(IEventBus bus) { DeferredRegister BLOCK = DeferredRegister.create(BuiltInRegistries.BLOCK, ParticleStorm.MODID); DeferredRegister> ENTITY = DeferredRegister.create(BuiltInRegistries.BLOCK_ENTITY_TYPE, ParticleStorm.MODID); DeferredHolder TEST = BLOCK.register("test_block", TestBlock::new); - TEST_ENTITY = ENTITY.register("test_entity", () -> BlockEntityType.Builder.of(TestBlock.Entity::new, TEST.get()).build(null)); + TEST_ENTITY = ENTITY.register("test_entity", () -> BlockEntityType.Builder.of(TestBlock.Entity::new, TEST.get()).build(DSL.remainderType())); BLOCK.register(bus); ENTITY.register(bus); } diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java index 435b65c..e95309b 100644 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java @@ -1,10 +1,11 @@ package org.mesdag.particlestorm.api.geckolib; import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.Nullable; import software.bernie.geckolib.animatable.GeoReplacedEntity; public interface GeoWithCurrentEntity extends GeoReplacedEntity { - Entity getCurrentEntity(); + @Nullable Entity getCurrentEntity(); void setCurrentEntity(Entity entity); } diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java index 784dce7..ddc3116 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java @@ -122,7 +122,7 @@ private static boolean hasSpaceInParticleLimit(ParticleEmitter emitter) { /// This component spawns particles using a disc shape, particles can be spawned inside the shape or on its outer perimeter. public static final class Disc extends EmitterShape { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - FloatMolangExp3.CODEC.fieldOf("offset").orElseGet(() -> FloatMolangExp3.ZERO).forGetter(disc -> disc.offset), + FloatMolangExp3.CODEC.fieldOf("offset").orElse(FloatMolangExp3.ZERO).forGetter(disc -> disc.offset), FloatMolangExp.CODEC.fieldOf("radius").orElse(FloatMolangExp.ONE).forGetter(disc -> disc.radius), PlaneNormal.CODEC.fieldOf("plane_normal").orElse(PlaneNormal.Y).forGetter(disc -> disc.planeNormal), Direction.CODEC.fieldOf("direction").orElse(Direction.OUTWARDS).forGetter(disc -> disc.direction), @@ -247,7 +247,11 @@ public Codec codec() { @Override public List getAllMolangExp() { - return List.of(direction.direct.exp1(), direction.direct.exp2(), direction.direct.exp3()); + return List.of( + offset.exp1(), offset.exp2(), offset.exp3(), + halfDimensions.exp1(), halfDimensions.exp2(), halfDimensions.exp3(), + direction.direct.exp1(), direction.direct.exp2(), direction.direct.exp3() + ); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleAppearanceLighting.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleAppearanceLighting.java index f929548..3b817fc 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleAppearanceLighting.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleAppearanceLighting.java @@ -14,6 +14,8 @@ public final class ParticleAppearanceLighting implements IParticleComponent { public static final ParticleAppearanceLighting INSTANCE = new ParticleAppearanceLighting(); public static final Codec CODEC = MapCodec.of(Encoder.empty(), Decoder.unit(ParticleAppearanceLighting.INSTANCE)).codec(); + private ParticleAppearanceLighting() {} + @Override public Codec codec() { return CODEC; diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java index c238685..260faff 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java @@ -35,7 +35,14 @@ /// Note that this must be less than or equal to 1/2 block /// @param expireOnContact Triggers expiration on contact if true /// @param events Triggers an event array of individual events -public record ParticleMotionCollision(BoolMolangExp enabled, float collisionDrag, float coefficientOfRestitution, float collisionRadius, boolean expireOnContact, List events) implements IParticleComponent { +public record ParticleMotionCollision( + BoolMolangExp enabled, + float collisionDrag, + float coefficientOfRestitution, + float collisionRadius, + boolean expireOnContact, + List events +) implements IParticleComponent { public static final ResourceLocation ID = ResourceLocation.withDefaultNamespace("particle_motion_collision"); public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( BoolMolangExp.CODEC.lenientOptionalFieldOf("enabled", BoolMolangExp.TRUE).forGetter(ParticleMotionCollision::enabled), @@ -92,10 +99,8 @@ public String toString() { '}'; } - /** - * @param event Triggers the specified event if the conditions are met - * @param minSpeed Optional minimum speed for event triggering - */ + /// @param event Triggers the specified event if the conditions are met + /// @param minSpeed Optional minimum speed for event triggering public record Event(String event, float minSpeed) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.STRING.fieldOf("event").forGetter(Event::event), diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionDynamic.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionDynamic.java index ba974cb..eee3159 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionDynamic.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionDynamic.java @@ -35,8 +35,12 @@ /// Useful to slow a rotation, or to limit the rotation acceleration

/// Think of a disc that speeds up (acceleration) but reaches a terminal speed (drag)

/// Another use is if you have a particle growing in size, having the rotation slow down due to drag can add "weight" to the particle's motion -public record ParticleMotionDynamic(FloatMolangExp3 linerAcceleration, FloatMolangExp linearDragCoefficient, FloatMolangExp rotationAcceleration, - FloatMolangExp rotationDragCoefficient) implements IParticleComponent { +public record ParticleMotionDynamic( + FloatMolangExp3 linerAcceleration, + FloatMolangExp linearDragCoefficient, + FloatMolangExp rotationAcceleration, + FloatMolangExp rotationDragCoefficient +) implements IParticleComponent { public static final ResourceLocation ID = ResourceLocation.withDefaultNamespace("particle_motion_dynamic"); public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( FloatMolangExp3.CODEC.fieldOf("linear_acceleration").orElse(FloatMolangExp3.ZERO).forGetter(ParticleMotionDynamic::linerAcceleration), diff --git a/src/main/java/org/mesdag/particlestorm/data/event/EventRandomize.java b/src/main/java/org/mesdag/particlestorm/data/event/EventRandomize.java index 43d391d..d202c1b 100644 --- a/src/main/java/org/mesdag/particlestorm/data/event/EventRandomize.java +++ b/src/main/java/org/mesdag/particlestorm/data/event/EventRandomize.java @@ -18,6 +18,7 @@ public final class EventRandomize implements IEventNode { public final List>> sortedNodes; + @SuppressWarnings({"rawtypes", "unchecked"}) public EventRandomize(List> nodes) { this.nodes = nodes; diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/BoolMolangExp.java b/src/main/java/org/mesdag/particlestorm/data/molang/BoolMolangExp.java index f552e5e..2184f3f 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/BoolMolangExp.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/BoolMolangExp.java @@ -2,7 +2,6 @@ import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; -import org.jetbrains.annotations.Nullable; import org.mesdag.particlestorm.api.MolangInstance; public class BoolMolangExp extends MolangExp { @@ -14,7 +13,7 @@ public class BoolMolangExp extends MolangExp { ); private final boolean constant; - public BoolMolangExp(boolean constant, @Nullable String expression) { + public BoolMolangExp(boolean constant, String expression) { super(expression); this.constant = constant; } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExpList.java b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExpList.java index 3465921..5fe2863 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExpList.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExpList.java @@ -8,39 +8,27 @@ import java.util.List; import java.util.Objects; -public class FloatMolangExpList { +public record FloatMolangExpList(int size, List expressions) { public static final FloatMolangExpList EMPTY = new FloatMolangExpList(0, List.of()); public static final Codec CODEC = Codec.either(FloatMolangExp.CODEC, Codec.list(FloatMolangExp.CODEC)).xmap( either -> either.map(e -> new FloatMolangExpList(1, Collections.singletonList(e)), l -> new FloatMolangExpList(l.size(), l)), list -> list.size() == 1 ? Either.left(list.expressions.getFirst()) : Either.right(list.expressions) ); - private final int size; - private final List expressions; - public FloatMolangExpList(int size, List expressions) { + public FloatMolangExpList { if (size != expressions.size()) { throw new IllegalArgumentException("Size of " + size + " not match the size of expressions"); } - this.size = size; - this.expressions = expressions; } public FloatMolangExpList(int size, FloatMolangExp... expressions) { this(size, Arrays.stream(expressions).toList()); } - public int size() { - return size; - } - public boolean isEmpty() { return size == 0; } - public List getExpressions() { - return expressions; - } - public boolean isSingleExp() { return size == 1; } diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java index 599e64c..6368223 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java @@ -12,7 +12,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; -import net.neoforged.fml.ModList; +import net.neoforged.fml.loading.LoadingModList; import org.joml.Quaternionf; import org.joml.Vector3f; import org.mesdag.particlestorm.api.IEventNode; @@ -27,7 +27,7 @@ public class MolangParticleInstance extends TextureSheetParticle implements IMolangParticleInstance { public static final int FULL_LIGHT = 0xF000F0; - private static final boolean isSodiumLoaded = ModList.get().isLoaded("sodium"); + private static final boolean isSodiumLoaded = LoadingModList.get().getModFileById("sodium") != null; protected final ParticlePreset preset; protected ParticleVariableTable vars; diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java index 7165583..b0b90fd 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java @@ -150,7 +150,7 @@ public void removeEmitter(ParticleEmitter emitter, boolean sync) { removeEmitter(emitter.id, sync); } - public ParticleEmitter removeEmitter(int id, boolean sync) { + public @Nullable ParticleEmitter removeEmitter(int id, boolean sync) { ParticleEmitter removed = emitters.remove(id); if (removed != null) { removed.onRemove(); diff --git a/src/main/resources/particlestorm.mixins.json b/src/main/resources/particlestorm.mixins.json index 03e654f..e582d26 100644 --- a/src/main/resources/particlestorm.mixins.json +++ b/src/main/resources/particlestorm.mixins.json @@ -2,11 +2,9 @@ "required": true, "minVersion": "0.8", "package": "org.mesdag.particlestorm.mixin", - "compatibilityLevel": "JAVA_17", + "compatibilityLevel": "JAVA_21", "refmap": "particlestorm.refmap.json", - "mixins": [ - "integration.geckolib.MolangQueriesAccessor" - ], + "mixins": [], "client": [ "BlockEntityMixin", "EntityMixin", @@ -20,6 +18,7 @@ "integration.geckolib.BakedModelFactory$BuiltinMixin", "integration.geckolib.GeoBoneMixin", "integration.geckolib.GeoReplacedEntityRendererMixin", + "integration.geckolib.MolangQueriesAccessor", "integration.geckolib.MolangQueriesMixin", "integration.geckolib.ParticleKeyframeDataMixin" ], From 7dbbd25b99d7d9674d426972a1148ed0259115cd Mon Sep 17 00:00:00 2001 From: westernat Date: Sun, 28 Dec 2025 15:27:46 +0800 Subject: [PATCH 02/13] =?UTF-8?q?=E4=BF=AE=E8=8D=AF=E6=B0=B4=E6=95=88?= =?UTF-8?q?=E6=9E=9C=E7=B2=92=E5=AD=90=E9=80=BB=E8=BE=91=E7=AB=AF=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java | 3 ++- .../particlestorm/api/geckolib/GeoWithCurrentEntity.java | 2 +- .../java/org/mesdag/particlestorm/mixin/LivingEntityMixin.java | 2 +- .../mixin/integration/geckolib/AnimationControllerMixin.java | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gradle.properties b/gradle.properties index 83a3567..bd38c29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.1.4.2 +mod_version=1.1.4.3 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java index 18206d8..4fbce37 100644 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java @@ -15,6 +15,7 @@ import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; +import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.ParticleStorm; @@ -126,7 +127,7 @@ public static boolean processParticleEffect(Object a, Object c, Object d) { return false; } - public static void setCurrentEntity(Object animatable, Entity entity) { + public static void setCurrentEntity(Object animatable, @Nullable Entity entity) { if (animatable instanceof GeoWithCurrentEntity withCurrentEntity) { withCurrentEntity.setCurrentEntity(entity); } diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java index e95309b..8960f9e 100644 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java @@ -7,5 +7,5 @@ public interface GeoWithCurrentEntity extends GeoReplacedEntity { @Nullable Entity getCurrentEntity(); - void setCurrentEntity(Entity entity); + void setCurrentEntity(@Nullable Entity entity); } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/LivingEntityMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/LivingEntityMixin.java index 919916b..a244439 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/LivingEntityMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/LivingEntityMixin.java @@ -14,7 +14,7 @@ public abstract class LivingEntityMixin { @WrapWithCondition(method = "tickEffects", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addParticle(Lnet/minecraft/core/particles/ParticleOptions;DDDDDD)V")) private boolean modify(Level instance, ParticleOptions particleData, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { - if (particleData instanceof MolangParticleOption(ResourceLocation id)) { + if (instance.isClientSide && particleData instanceof MolangParticleOption(ResourceLocation id)) { PSGameClient.LOADER.addTrackedEmitter((LivingEntity) (Object) this, id); return false; } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationControllerMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationControllerMixin.java index 297ba30..9c8a1a1 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationControllerMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationControllerMixin.java @@ -50,7 +50,7 @@ public abstract class AnimationControllerMixin implemen } @WrapWithCondition(method = "processCurrentAnimation", at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;log(Lorg/apache/logging/log4j/Level;Ljava/lang/String;)V", ordinal = 1)) - private boolean processParticleEffect(Logger instance, Level level, String s, @Local(argsOnly = true, ordinal = 0) double adjustedTick, @Local ParticleKeyframeData keyframeData) { + private boolean processParticleEffect(Logger instance, Level level, String s, @Local(name = "keyframeData") ParticleKeyframeData keyframeData) { return GeckoLibHelper.processParticleEffect(animatable, this, keyframeData); } From e025a2b13bd9a8e55eb7601b56667460ad414bf7 Mon Sep 17 00:00:00 2001 From: westernat Date: Sat, 7 Mar 2026 15:49:53 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=E6=94=AF=E6=8C=81query.is=5Fblock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- gradle.properties | 2 +- .../api/geckolib/GeckoLibHelper.java | 10 ++-- .../data/molang/compiler/MolangParser.java | 11 +++- .../data/molang/compiler/MolangQueries.java | 19 ++++--- .../function/misc/IsBlockFunction.java | 53 +++++++++++++++++++ .../molang/compiler/value/StringValue.java | 16 ++++++ .../mixed/IAnimatableInstanceCache.java | 8 ++- .../AnimatableInstanceCacheMixin.java | 36 ++++--------- .../integration/geckolib/GeoBoneMixin.java | 13 ++--- 10 files changed, 116 insertions(+), 54 deletions(-) create mode 100644 src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/IsBlockFunction.java create mode 100644 src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/StringValue.java diff --git a/build.gradle b/build.gradle index 4cd5b76..c022baa 100644 --- a/build.gradle +++ b/build.gradle @@ -88,7 +88,7 @@ repositories { dependencies { compileOnly "software.bernie.geckolib:geckolib-neoforge-${minecraft_version}:${geckolib_version}" -// localRuntime "software.bernie.geckolib:geckolib-neoforge-${minecraft_version}:${geckolib_version}" + localRuntime "software.bernie.geckolib:geckolib-neoforge-${minecraft_version}:${geckolib_version}" } tasks.withType(ProcessResources).configureEach { diff --git a/gradle.properties b/gradle.properties index bd38c29..ea68834 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.1.4.3 +mod_version=1.1.5 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java index 4fbce37..3c6c6e0 100644 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java @@ -102,9 +102,8 @@ public static boolean processParticleEffect(Object a, Object c, Object d) { ResourceLocation particle = iData.particlestorm$getParticle(); MolangExp expression = iData.particlestorm$getExpression(variableTable); IAnimatableInstanceCache cache = IAnimatableInstanceCache.of(animatable.getAnimatableInstanceCache()); - for (GeoBone geoBone : bones) { - IGeoBone bone = IGeoBone.of(geoBone); - LocatorValue locator = bone.particlestorm$getLocators().get(keyframeData.getLocator()); + for (GeoBone bone : bones) { + LocatorValue locator = IGeoBone.of(bone).particlestorm$getLocators().get(keyframeData.getLocator()); if (locator == null) continue; ParticleEmitter current = PSGameClient.LOADER.getEmitter(cache.particlestorm$getCachedId().getInt(locator)); @@ -119,8 +118,9 @@ public static boolean processParticleEffect(Object a, Object c, Object d) { double[] rotation = getLocatorRotation(locator); emitter.offsetPos = new Vec3(offset[0] * 0.0625, offset[1] * 0.0625, -offset[2] * 0.0625); emitter.offsetRot = new Vector3f((float) Math.toRadians(rotation[0]), (float) Math.toRadians(rotation[1]), (float) Math.toRadians(rotation[2])); - emitter.parentPosition = cache.particlestorm$getPosition(); - emitter.parentRotation = cache.particlestorm$getRotation(); + Vector3f[] transform = cache.particlestorm$getTransform(bone); + emitter.parentPosition = transform[0]; + emitter.parentRotation = transform[1]; emitter.parentMode = ParticleEmitter.ParentMode.LOCATOR; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangParser.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangParser.java index 191f095..0a4d414 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangParser.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangParser.java @@ -10,6 +10,7 @@ import org.mesdag.particlestorm.data.molang.compiler.function.limit.ClampFunction; import org.mesdag.particlestorm.data.molang.compiler.function.limit.MaxFunction; import org.mesdag.particlestorm.data.molang.compiler.function.limit.MinFunction; +import org.mesdag.particlestorm.data.molang.compiler.function.misc.IsBlockFunction; import org.mesdag.particlestorm.data.molang.compiler.function.misc.PiFunction; import org.mesdag.particlestorm.data.molang.compiler.function.misc.ToDegFunction; import org.mesdag.particlestorm.data.molang.compiler.function.misc.ToRadFunction; @@ -31,7 +32,7 @@ import static org.mesdag.particlestorm.data.molang.compiler.MolangQueries.applyPrefixAliases; public class MolangParser { - private static final Pattern EXPRESSION_FORMAT = Pattern.compile("^[\\w\\s_+-/*%^&|<>=!?:.,()]+$"); + private static final Pattern EXPRESSION_FORMAT = Pattern.compile("^[\\w\\s_+-/*%^&|<>=!?:.,()']+$"); private static final Pattern WHITESPACE = Pattern.compile("\\s"); private static final Pattern NUMERIC = Pattern.compile("^-?\\d+(\\.\\d+)?$"); private static final String MOLANG_RETURN = "return "; @@ -66,6 +67,7 @@ public class MolangParser { map.put("math.to_deg", ToDegFunction::new); map.put("math.to_rad", ToRadFunction::new); map.put("math.trunc", TruncateFunction::new); + map.put("query.is_block", IsBlockFunction::new); }); private final VariableTable table; @@ -247,7 +249,7 @@ public List>> compileSymbols(char[] chars) { public MathValue parseSymbols(List>> symbols) throws IllegalArgumentException { if (symbols.size() == 2) { - Optional prefix = symbols.getFirst().left().filter(left -> left.startsWith("-") || left.startsWith("!") || isFunctionRegistered(left)); + Optional prefix = symbols.getFirst().left().filter(left -> left.startsWith("-") || left.startsWith("!") || isFunctionRegistered(MolangQueries.applyQueryAliases(left))); Optional> group = symbols.get(1).right(); if (prefix.isPresent() && group.isPresent()) @@ -287,6 +289,9 @@ protected MathValue compileSingleValue(Either> symbol) t if (isNumeric(string)) return new Constant(Double.parseDouble(string)); + if (string.startsWith("'") && string.endsWith("'")) + return new StringValue(string.substring(1, string.length() - 1)); + if (isLikelyVariable(string)) { if (string.startsWith("-")) return new Negative(getVariableFor(string.substring(1))); @@ -390,6 +395,8 @@ protected MathValue compileFunction(String name, List args) throws Il return new Negative(compileFunction(name.substring(1), args)); } + name = MolangQueries.applyQueryAliases(name); + if (!isFunctionRegistered(name)) return null; diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java index 091142a..1c200fa 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java @@ -1,5 +1,6 @@ package org.mesdag.particlestorm.data.molang.compiler; +import com.google.common.collect.ImmutableMap; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.neoforged.fml.ModLoader; @@ -9,14 +10,13 @@ import org.mesdag.particlestorm.data.molang.compiler.value.Variable; import java.util.Collection; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.ToDoubleFunction; public final class MolangQueries { - private static final Map UNFROZEN_QUERIES = new ConcurrentHashMap<>(); - private static final Map FROZEN_QUERIES = new HashMap<>(); + private static Map UNFROZEN_QUERIES = new ConcurrentHashMap<>(); + private static Map FROZEN_QUERIES = ImmutableMap.of(); static { setDefaultQueryValues(); @@ -33,7 +33,7 @@ public static void registerVariable(String name, Variable variable) { } static Variable getQueryFor(String name) { - return FROZEN_QUERIES.getOrDefault(applyPrefixAliases(name, "query.", "q."), new Variable(name, 0)); + return FROZEN_QUERIES.getOrDefault(applyQueryAliases(name), new Variable(name, 0)); } private static void registerQueryVariable(String name, ToDoubleFunction value) { @@ -69,6 +69,13 @@ public static String applyPrefixAliases(String text, String properName, String.. return text; } + public static String applyQueryAliases(String text) { + if (text.startsWith("q.")) { + return "query" + text.substring(1); + } + return text; + } + private static void setDefaultQueryValues() { registerQueryVariable("query.cardinal_player_facing", p -> Minecraft.getInstance().player == null ? 0.0 : Minecraft.getInstance().player.getDirection().ordinal()); registerQueryVariable("query.day", p -> p.getLevel().getGameTime() / 24000d); @@ -88,7 +95,7 @@ private static void setDefaultQueryValues() { registerQueryVariable("query.attached_yo", p -> p.getAttachedEntity() == null ? 0.0 : p.getAttachedEntity().yo); registerQueryVariable("query.attached_zo", p -> p.getAttachedEntity() == null ? 0.0 : p.getAttachedEntity().zo); ModLoader.postEvent(new RegisterMolangQueriesEvent(MolangQueries::registerQueryVariable)); - FROZEN_QUERIES.putAll(UNFROZEN_QUERIES); - UNFROZEN_QUERIES.clear(); + FROZEN_QUERIES = ImmutableMap.copyOf(UNFROZEN_QUERIES); + UNFROZEN_QUERIES = null; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/IsBlockFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/IsBlockFunction.java new file mode 100644 index 0000000..966cce2 --- /dev/null +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/IsBlockFunction.java @@ -0,0 +1,53 @@ +package org.mesdag.particlestorm.data.molang.compiler.function.misc; + +import com.mojang.datafixers.util.Either; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.mesdag.particlestorm.api.MolangInstance; +import org.mesdag.particlestorm.data.molang.compiler.MathValue; +import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; +import org.mesdag.particlestorm.data.molang.compiler.value.StringValue; + +public final class IsBlockFunction extends MathFunction { + private final StringValue stringValue; + private final Either> either; + + public IsBlockFunction(MathValue... values) { + super(values); + if (values[0] instanceof StringValue stringValue) { + this.stringValue = stringValue; + String value = stringValue.value(); + this.either = value.startsWith("#") + ? Either.right(BlockTags.create(ResourceLocation.parse(value.substring(1)))) + : Either.left(BuiltInRegistries.BLOCK.get(ResourceLocation.parse(value))); + } else { + throw new IllegalArgumentException(values[0] + " is not a string value"); + } + } + + @Override + public String getName() { + return "query.is_block"; + } + + @Override + public double compute(MolangInstance instance) { + BlockState state = instance.getLevel().getBlockState(BlockPos.containing(instance.getPosition())); + return either.map(state::is, state::is) ? 1 : 0; + } + + @Override + public int getMinArgs() { + return 1; + } + + @Override + public MathValue[] getArgs() { + return new MathValue[]{stringValue}; + } +} diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/StringValue.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/StringValue.java new file mode 100644 index 0000000..d743da6 --- /dev/null +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/StringValue.java @@ -0,0 +1,16 @@ +package org.mesdag.particlestorm.data.molang.compiler.value; + +import org.mesdag.particlestorm.api.MolangInstance; +import org.mesdag.particlestorm.data.molang.compiler.MathValue; + +public record StringValue(String value) implements MathValue { + @Override + public double get(MolangInstance instance) { + return 1; + } + + @Override + public String toString() { + return value; + } +} diff --git a/src/main/java/org/mesdag/particlestorm/mixed/IAnimatableInstanceCache.java b/src/main/java/org/mesdag/particlestorm/mixed/IAnimatableInstanceCache.java index 6cc5084..1ad1667 100644 --- a/src/main/java/org/mesdag/particlestorm/mixed/IAnimatableInstanceCache.java +++ b/src/main/java/org/mesdag/particlestorm/mixed/IAnimatableInstanceCache.java @@ -3,16 +3,14 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import org.joml.Vector3f; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; +import software.bernie.geckolib.cache.object.GeoBone; import software.bernie.geckolib.loading.json.raw.LocatorValue; public interface IAnimatableInstanceCache { Object2IntMap particlestorm$getCachedId(); - Vector3f particlestorm$getPosition(); - - Vector3f particlestorm$getRotation(); - -// Vector3f particlestorm$getScale(); + /// \[position, rotation\] + Vector3f[] particlestorm$getTransform(GeoBone bone); static IAnimatableInstanceCache of(AnimatableInstanceCache cache) { return (IAnimatableInstanceCache) cache; diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimatableInstanceCacheMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimatableInstanceCacheMixin.java index e154110..11d5a3a 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimatableInstanceCacheMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimatableInstanceCacheMixin.java @@ -2,24 +2,24 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.joml.Vector3f; import org.mesdag.particlestorm.mixed.IAnimatableInstanceCache; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.Unique; +import software.bernie.geckolib.cache.object.GeoBone; import software.bernie.geckolib.loading.json.raw.LocatorValue; +import java.util.Map; + @Pseudo @Mixin(targets = "software.bernie.geckolib.animatable.instance.AnimatableInstanceCache", remap = false) -public class AnimatableInstanceCacheMixin implements IAnimatableInstanceCache { +public abstract class AnimatableInstanceCacheMixin implements IAnimatableInstanceCache { @Unique private Object2IntMap particlestorm$cachedId; @Unique - private Vector3f particlestorm$position; - @Unique - private Vector3f particlestorm$rotation; -// @Unique -// private Vector3f particlestorm$scale; + private Map particlestorm$transform; @Override public Object2IntMap particlestorm$getCachedId() { @@ -31,26 +31,10 @@ public class AnimatableInstanceCacheMixin implements IAnimatableInstanceCache { } @Override - public Vector3f particlestorm$getPosition() { - if (particlestorm$position == null) { - this.particlestorm$position = new Vector3f(); + public Vector3f[] particlestorm$getTransform(GeoBone bone) { + if (particlestorm$transform == null) { + this.particlestorm$transform = new Object2ObjectOpenHashMap<>(); } - return particlestorm$position; + return particlestorm$transform.computeIfAbsent(bone, b -> new Vector3f[]{new Vector3f(), new Vector3f()}); } - - @Override - public Vector3f particlestorm$getRotation() { - if (particlestorm$rotation == null) { - this.particlestorm$rotation = new Vector3f(); - } - return particlestorm$rotation; - } - -// @Override -// public Vector3f particlestorm$getScale() { -// if (particlestorm$scale == null) { -// this.particlestorm$scale = new Vector3f(); -// } -// return particlestorm$scale; -// } } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoBoneMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoBoneMixin.java index 05c197e..0a6fab3 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoBoneMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoBoneMixin.java @@ -1,5 +1,6 @@ package org.mesdag.particlestorm.mixin.integration.geckolib; +import org.joml.Vector3f; import org.mesdag.particlestorm.mixed.IAnimatableInstanceCache; import org.mesdag.particlestorm.mixed.IGeoBone; import org.spongepowered.asm.mixin.Mixin; @@ -10,6 +11,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import software.bernie.geckolib.animatable.GeoAnimatable; +import software.bernie.geckolib.cache.object.GeoBone; import software.bernie.geckolib.loading.json.raw.LocatorValue; import software.bernie.geckolib.loading.math.MolangQueries; @@ -30,12 +32,6 @@ public abstract class GeoBoneMixin implements IGeoBone { private float rotY; @Shadow private float rotZ; -// @Shadow -// private float scaleX; -// @Shadow -// private float scaleY; -// @Shadow -// private float scaleZ; @Unique private Map particlestorm$locators; @@ -55,8 +51,9 @@ private void setData(CallbackInfo ci) { MolangQueries.Actor actor = MolangQueriesAccessor.callGetActor(); if (actor != null && actor.animatable() instanceof GeoAnimatable animatable) { IAnimatableInstanceCache cache = IAnimatableInstanceCache.of(animatable.getAnimatableInstanceCache()); - cache.particlestorm$getPosition().set(positionX, positionY, positionZ); - cache.particlestorm$getRotation().set(rotX, rotY, rotZ); + Vector3f[] transform = cache.particlestorm$getTransform((GeoBone) (Object) this); + transform[0].set(positionX, positionY, positionZ); + transform[1].set(rotX, rotY, rotZ); //cache.particlestorm$getScale().set(scaleX, scaleY, scaleZ); todo } } From a28fb20284b26ea9df577710d0d27b8131bb2bcc Mon Sep 17 00:00:00 2001 From: westernat Date: Tue, 28 Apr 2026 11:48:54 +0800 Subject: [PATCH 04/13] 1.1.6.2 --- build.gradle | 11 +++--- gradle.properties | 10 ++--- .../api/IMolangParticleInstance.java | 4 ++ .../data/component/EmitterShape.java | 3 ++ .../component/ParticleExpireIfInBlocks.java | 38 +++++++++---------- .../ParticleExpireIfNotInBlocks.java | 38 +++++++++---------- .../particle/MolangParticleInstance.java | 10 +++++ .../particle/MolangParticleLoader.java | 11 +++--- .../resources/META-INF/neoforge.mods.toml | 4 +- 9 files changed, 68 insertions(+), 61 deletions(-) diff --git a/build.gradle b/build.gradle index c022baa..6ece24e 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'eclipse' id 'idea' id 'maven-publish' - id 'net.neoforged.moddev' version '2.0.88' + id 'net.neoforged.moddev' version '2.0.140' } version = mod_version @@ -28,10 +28,7 @@ neoForge { mappingsVersion = project.parchment_mappings_version minecraftVersion = project.parchment_minecraft_version } - - accessTransformers { - file('src/main/resources/META-INF/accesstransformer.cfg') - } + validateAccessTransformers = true runs { client { @@ -55,7 +52,9 @@ neoForge { // 忽略无效指令,避免有的人没安装 JetBrain Runtime 无法启动游戏 "-XX:+IgnoreUnrecognizedVMOptions", // 启用 JetBrain Runtime 热重载功能 - "-XX:+AllowEnhancedClassRedefinition" + "-XX:+AllowEnhancedClassRedefinition", + // 并发修改检测 + //"-javaagent:mods/CMESuckMyDuck-1.0.5.jar=net/minecraft/util/ClassInstanceMultiMap;byClass;Map;nonstatic" ]) if (isRunningInIdea) { systemProperty "terminal.jline", "true" diff --git a/gradle.properties b/gradle.properties index ea68834..981fe80 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.1.5 +mod_version=1.1.6.2 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html @@ -43,7 +43,7 @@ mod_description=Uses a Bedrock Edition JSON format for particle effects. geckolib_version=4.8.2 -#systemProp.http.proxyHost=localhost -#systemProp.http.proxyPort=7890 -#systemProp.https.proxyHost=localhost -#systemProp.https.proxyPort=7890 +systemProp.http.proxyHost=localhost +systemProp.http.proxyPort=7897 +systemProp.https.proxyHost=localhost +systemProp.https.proxyPort=7897 diff --git a/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java b/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java index 01cc805..7d12ce9 100644 --- a/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java +++ b/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java @@ -102,6 +102,10 @@ default Particle self() { void setCollision(boolean bool); + void discard(); + + boolean isDiscarded(); + // region default default void moveDirectly(double x, double y, double z) { self().setBoundingBox(self().getBoundingBox().move(x, y, z)); diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java index ddc3116..f86d0a6 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java @@ -109,6 +109,9 @@ private void emittingParticle(Par for (IParticleComponent component : particlePreset.effect.orderedParticleComponents) { component.apply(instance); } + if (instance.isDiscarded()) { + return; + } instance.setComponents(particlePreset.effect.orderedParticleComponentsWhichRequireUpdate); if (!particlePreset.motionDynamic) instance.setParticleSpeed(0.0, 0.0, 0.0); Minecraft.getInstance().particleEngine.add(instance); diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfInBlocks.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfInBlocks.java index 001a438..87e92f4 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfInBlocks.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfInBlocks.java @@ -1,15 +1,12 @@ package org.mesdag.particlestorm.data.component; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.serialization.Codec; -import net.minecraft.commands.arguments.blocks.BlockStateParser; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.registries.Registries; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import org.mesdag.particlestorm.ParticleStorm; +import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.IParticleComponent; import org.mesdag.particlestorm.data.molang.MolangExp; @@ -26,11 +23,11 @@ public final class ParticleExpireIfInBlocks implements IParticleComponent { blocks -> List.copyOf(blocks.ids) ); public final Set ids; - public final Set states; + public final Set blocks; public ParticleExpireIfInBlocks(Set ids) { this.ids = ids; - this.states = new HashSet<>(); + this.blocks = new HashSet<>(); } @Override @@ -45,23 +42,22 @@ public List getAllMolangExp() { @Override public void initialize(Level level) { - if (states.isEmpty()) { - try { - HolderLookup lookup = level.holderLookup(Registries.BLOCK); - for (String id : ids) { - BlockStateParser.BlockResult result = BlockStateParser.parseForBlock(lookup, id, false); - states.add(result.blockState()); - } - } catch (CommandSyntaxException e) { - states.add(Blocks.AIR.defaultBlockState()); - ParticleStorm.LOGGER.error(e.getMessage()); + if (blocks.isEmpty()) { + for (String id : ids) { + BuiltInRegistries.BLOCK.getOptional(ResourceLocation.parse(id)).ifPresent(blocks::add); } } } + @Override + public void apply(IMolangParticleInstance instance) { + if (!blocks.contains(instance.getLevel().getBlockState(BlockPos.containing(instance.getPosition())).getBlock())) { + instance.discard(); + } + } + @Override public String toString() { - return "ParticleExpireIfInBlocks[" + - "blocks=" + ids + ']'; + return "ParticleExpireIfInBlocks[blocks=" + ids + ']'; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfNotInBlocks.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfNotInBlocks.java index 03cbcfc..830c1ab 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfNotInBlocks.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfNotInBlocks.java @@ -1,15 +1,12 @@ package org.mesdag.particlestorm.data.component; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.serialization.Codec; -import net.minecraft.commands.arguments.blocks.BlockStateParser; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.registries.Registries; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import org.mesdag.particlestorm.ParticleStorm; +import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.IParticleComponent; import org.mesdag.particlestorm.data.molang.MolangExp; @@ -23,11 +20,11 @@ public final class ParticleExpireIfNotInBlocks implements IParticleComponent { blocks -> List.copyOf(blocks.ids) ); private final Set ids; - public final Set states; + public final Set blocks; public ParticleExpireIfNotInBlocks(Set ids) { this.ids = ids; - this.states = new HashSet<>(); + this.blocks = new HashSet<>(); } @Override @@ -42,23 +39,22 @@ public List getAllMolangExp() { @Override public void initialize(Level level) { - if (states.isEmpty()) { - try { - HolderLookup lookup = level.holderLookup(Registries.BLOCK); - for (String id : ids) { - BlockStateParser.BlockResult result = BlockStateParser.parseForBlock(lookup, id, false); - states.add(result.blockState()); - } - } catch (CommandSyntaxException e) { - states.add(Blocks.AIR.defaultBlockState()); - ParticleStorm.LOGGER.error(e.getMessage()); + if (blocks.isEmpty()) { + for (String id : ids) { + BuiltInRegistries.BLOCK.getOptional(ResourceLocation.parse(id)).ifPresent(blocks::add); } } } + @Override + public void apply(IMolangParticleInstance instance) { + if (!blocks.contains(instance.getLevel().getBlockState(BlockPos.containing(instance.getPosition())).getBlock())) { + instance.discard(); + } + } + @Override public String toString() { - return "ParticleExpireIfNotInBlocks[" + - "blocks=" + ids + ']'; + return "ParticleExpireIfNotInBlocks[blocks=" + ids + ']'; } } diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java index 6368223..d5d9cd7 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java @@ -313,6 +313,16 @@ public void setCollision(boolean bool) { this.hasCollision = bool; } + @Override + public void discard() { + remove(); + } + + @Override + public boolean isDiscarded() { + return removed; + } + @Override public VariableTable getVars() { return vars; diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java index b0b90fd..f0ba977 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java @@ -26,10 +26,7 @@ import net.neoforged.fml.ModLoader; import org.jetbrains.annotations.Nullable; import org.mesdag.particlestorm.ParticleStorm; -import org.mesdag.particlestorm.api.IParticleComponent; -import org.mesdag.particlestorm.api.IntAllocator; -import org.mesdag.particlestorm.api.MolangParticleLoadEvent; -import org.mesdag.particlestorm.api.RegisterCustomEmitterTypeEvent; +import org.mesdag.particlestorm.api.*; import org.mesdag.particlestorm.data.DefinedParticleEffect; import org.mesdag.particlestorm.network.EmitterRemovalPacket; import org.mesdag.particlestorm.network.EmitterSynchronizePacket; @@ -69,8 +66,10 @@ public Map id2Emitter() { public void tick(LocalPlayer localPlayer) { if (!initialized) { for (ParticlePreset detail : id2Particle.values()) { - for (IParticleComponent component : detail.effect.orderedParticleComponents) { - component.initialize(localPlayer.level()); + for (IComponent component : detail.effect.orderedComponents) { + if (component instanceof IParticleComponent particleComponent) { + particleComponent.initialize(localPlayer.level()); + } } } removeAll(); diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index 8f1b199..646dc83 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -47,8 +47,8 @@ config="${mod_id}.mixins.json" # The [[accessTransformers]] block allows you to declare where your AT file is. # If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg -#[[accessTransformers]] -#file="META-INF/accesstransformer.cfg" +[[accessTransformers]] +file="META-INF/accesstransformer.cfg" # The coremods config file path is not configurable and is always loaded from META-INF/coremods.json From 319d90cfdcd70308dc25250ca2ebec80d911cd04 Mon Sep 17 00:00:00 2001 From: westernat Date: Tue, 28 Apr 2026 11:53:53 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=BB=A3=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index 981fe80..98bd11d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -43,7 +43,7 @@ mod_description=Uses a Bedrock Edition JSON format for particle effects. geckolib_version=4.8.2 -systemProp.http.proxyHost=localhost -systemProp.http.proxyPort=7897 -systemProp.https.proxyHost=localhost -systemProp.https.proxyPort=7897 +#systemProp.http.proxyHost=localhost +#systemProp.http.proxyPort=7897 +#systemProp.https.proxyHost=localhost +#systemProp.https.proxyPort=7897 From dcf17c41e1514d0926a8ece5a1bcb4fbe5b72ac6 Mon Sep 17 00:00:00 2001 From: westernat Date: Sat, 2 May 2026 13:10:22 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E4=BF=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../mesdag/particlestorm/particle/MolangParticleLoader.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index 98bd11d..f74b2d7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.1.6.2 +mod_version=1.1.6.3 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java index f0ba977..aa990dc 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java @@ -82,11 +82,11 @@ public void tick(LocalPlayer localPlayer) { ParticleEmitter emitter = iterator.next().getValue(); try { if (emitter.isRemoved() || emitter.level.dimension() != localPlayer.level().dimension()) { - allocator.release(emitter.id); emitter.onRemove(); emitter.remove(); iterator.remove(); - } else if (Mth.square(emitter.pos.x - localPlayer.getX()) + Mth.square(emitter.pos.z - localPlayer.getZ()) < renderDistSqr) { + allocator.release(emitter.id); + } else if (Mth.lengthSquared(emitter.pos.x - localPlayer.getX(), emitter.pos.z - localPlayer.getZ()) < renderDistSqr) { emitter.tick(); } } catch (Exception e) { From 7a5a724b63e4776e7b6e6a2aa7b8e8ec4b8bcbbb Mon Sep 17 00:00:00 2001 From: westernat Date: Thu, 14 May 2026 22:59:57 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=E4=BF=AE=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 6 +- .../mesdag/particlestorm/PSGameClient.java | 71 +++++++++++---- .../org/mesdag/particlestorm/PSModClient.java | 17 ---- .../mesdag/particlestorm/ParticleStorm.java | 1 + .../particlestorm/api/MolangInstance.java | 8 +- .../api/ParticlePresetLoadedEvent.java | 9 +- .../api/RegisterCustomComponentEvent.java | 18 ++++ .../api/RegisterCustomEventNodeEvent.java | 13 +++ .../api/RegisterCustomMaterialEvent.java | 19 ++++ .../api/RegisterMolangQueriesEvent.java | 7 +- .../particlestorm/api/ToFloatFunction.java | 6 ++ .../mesdag/particlestorm/data/MathHelper.java | 6 +- .../data/component/EmitterInitialization.java | 12 ++- .../data/description/DescriptionMaterial.java | 54 ++++++++--- .../data/molang/FloatMolangExp.java | 2 +- .../data/molang/FloatMolangExp2.java | 5 + .../data/molang/FloatMolangExp3.java | 6 ++ .../data/molang/FloatMolangExpList.java | 6 ++ .../particlestorm/data/molang/MolangExp.java | 12 ++- .../data/molang/VariableTable.java | 8 +- .../data/molang/compiler/MathValue.java | 41 ++++----- .../data/molang/compiler/MolangParser.java | 2 +- .../data/molang/compiler/MolangQueries.java | 38 ++++---- .../data/molang/compiler/Operator.java | 86 +++++++----------- .../compiler/function/MathFunction.java | 67 ++++++-------- .../function/generic/ACosFunction.java | 19 ++-- .../function/generic/ASinFunction.java | 19 ++-- .../function/generic/ATan2Function.java | 19 ++-- .../function/generic/ATanFunction.java | 17 ++-- .../function/generic/AbsFunction.java | 15 ++- .../function/generic/CosFunction.java | 19 ++-- .../function/generic/ExpFunction.java | 19 ++-- .../function/generic/LogFunction.java | 19 ++-- .../function/generic/ModFunction.java | 19 ++-- .../function/generic/PowFunction.java | 19 ++-- .../function/generic/SinFunction.java | 17 ++-- .../function/generic/SqrtFunction.java | 19 ++-- .../function/limit/ClampFunction.java | 19 ++-- .../compiler/function/limit/MaxFunction.java | 19 ++-- .../compiler/function/limit/MinFunction.java | 19 ++-- .../function/misc/IsBlockFunction.java | 2 +- .../compiler/function/misc/PiFunction.java | 20 ++-- .../compiler/function/misc/ToDegFunction.java | 20 ++-- .../compiler/function/misc/ToRadFunction.java | 20 ++-- .../function/random/DieRollFunction.java | 42 ++++----- .../random/DieRollIntegerFunction.java | 29 +++--- .../function/random/RandomFunction.java | 39 ++++---- .../random/RandomIntegerFunction.java | 29 +++--- .../compiler/function/round/CeilFunction.java | 18 ++-- .../function/round/FloorFunction.java | 18 ++-- .../function/round/HermiteBlendFunction.java | 17 ++-- .../compiler/function/round/LerpFunction.java | 15 ++- .../function/round/LerpRotFunction.java | 19 ++-- .../function/round/RoundFunction.java | 15 ++- .../function/round/TruncateFunction.java | 19 ++-- .../molang/compiler/value/BooleanNegate.java | 20 ++-- .../molang/compiler/value/Calculation.java | 45 +++------ .../molang/compiler/value/CompoundValue.java | 24 +++-- .../data/molang/compiler/value/Constant.java | 20 ++-- .../data/molang/compiler/value/Group.java | 20 ++-- .../data/molang/compiler/value/Negative.java | 20 ++-- .../molang/compiler/value/StringValue.java | 12 ++- .../data/molang/compiler/value/Ternary.java | 26 +++--- .../data/molang/compiler/value/Variable.java | 63 +++++++++---- .../compiler/value/VariableAssignment.java | 21 +++-- .../particlestorm/mixin/EntityMixin.java | 4 +- .../mixin/ParticleEngineMixin.java | 12 +++ .../particle/MolangParticleInstance.java | 48 +++++----- .../particle/ParticleEmitter.java | 32 +++---- .../particle/ParticlePreset.java | 30 ++++-- .../particle/ParticleVariableTable.java | 2 +- .../particle_definitions/blend.json | 55 +++++++++++ .../shaders/core/particle_no_discard.fsh | 24 +++++ .../shaders/core/particle_no_discard.json | 17 ++++ .../particlestorm/textures/particle/blend.png | Bin 498 -> 6141 bytes .../textures/particle/missing.png | Bin 209 -> 0 bytes .../particlestorm/textures/particle/test.png | Bin 328 -> 0 bytes 77 files changed, 890 insertions(+), 724 deletions(-) delete mode 100644 src/main/java/org/mesdag/particlestorm/PSModClient.java create mode 100644 src/main/java/org/mesdag/particlestorm/api/RegisterCustomComponentEvent.java create mode 100644 src/main/java/org/mesdag/particlestorm/api/RegisterCustomEventNodeEvent.java create mode 100644 src/main/java/org/mesdag/particlestorm/api/RegisterCustomMaterialEvent.java create mode 100644 src/main/java/org/mesdag/particlestorm/api/ToFloatFunction.java create mode 100644 src/main/resources/assets/particlestorm/particle_definitions/blend.json create mode 100644 src/main/resources/assets/particlestorm/shaders/core/particle_no_discard.fsh create mode 100644 src/main/resources/assets/particlestorm/shaders/core/particle_no_discard.json delete mode 100644 src/main/resources/assets/particlestorm/textures/particle/missing.png delete mode 100644 src/main/resources/assets/particlestorm/textures/particle/test.png diff --git a/gradle.properties b/gradle.properties index f74b2d7..6e3a928 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ minecraft_version=1.21.1 # as they do not follow standard versioning conventions. minecraft_version_range=[1.21.1,1.22) # The Neo version must agree with the Minecraft version to get a valid artifact -neo_version=21.1.170 +neo_version=21.1.219 # The Neo version range can use any version of Neo as bounds neo_version_range=[21,) # The loader version range can only use the major version of FML as bounds @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.1.6.3 +mod_version=1.2.0 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html @@ -41,7 +41,7 @@ mod_authors=Westernat # The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. mod_description=Uses a Bedrock Edition JSON format for particle effects. -geckolib_version=4.8.2 +geckolib_version=4.8.4 #systemProp.http.proxyHost=localhost #systemProp.http.proxyPort=7897 diff --git a/src/main/java/org/mesdag/particlestorm/PSGameClient.java b/src/main/java/org/mesdag/particlestorm/PSGameClient.java index 4c14122..f111377 100644 --- a/src/main/java/org/mesdag/particlestorm/PSGameClient.java +++ b/src/main/java/org/mesdag/particlestorm/PSGameClient.java @@ -10,36 +10,34 @@ import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.client.renderer.debug.DebugRenderer; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.util.Mth; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoader; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.event.config.ModConfigEvent; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.neoforge.client.event.ClientTickEvent; -import net.neoforged.neoforge.client.event.EntityRenderersEvent; -import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; -import net.neoforged.neoforge.common.NeoForge; -import org.mesdag.particlestorm.api.IComponent; -import org.mesdag.particlestorm.api.IEventNode; +import net.neoforged.neoforge.client.event.*; +import org.mesdag.particlestorm.api.*; import org.mesdag.particlestorm.api.geckolib.GeckoLibHelper; import org.mesdag.particlestorm.data.component.*; import org.mesdag.particlestorm.data.event.*; +import org.mesdag.particlestorm.particle.MolangParticleInstance; import org.mesdag.particlestorm.particle.MolangParticleLoader; import org.mesdag.particlestorm.particle.ParticleEmitter; -@EventBusSubscriber(modid = ParticleStorm.MODID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) +import java.io.IOException; + +@EventBusSubscriber(modid = ParticleStorm.MODID, value = Dist.CLIENT) public final class PSGameClient { public static final MolangParticleLoader LOADER = new MolangParticleLoader(); public static final ParticleRenderType PARTICLE_ADD = new ParticleRenderType() { @Override public BufferBuilder begin(Tesselator tesselator, TextureManager textureManager) { RenderSystem.enableDepthTest(); - Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer(); RenderSystem.depthMask(false); RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES); RenderSystem.enableBlend(); @@ -51,6 +49,32 @@ public String toString() { return "PARTICLE_ADD"; } }; + public static final ParticleRenderType PARTICLE_BLEND = new ParticleRenderType() { + @Override + public BufferBuilder begin(Tesselator tesselator, TextureManager textureManager) { + RenderSystem.enableDepthTest(); + RenderSystem.depthMask(false); + RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + return tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE); + } + + public String toString() { + return "PARTICLE_BLEND"; + } + }; + + private static ShaderInstance particleNoDiscard; + + public static ShaderInstance getParticleNoDiscardShader() { + return particleNoDiscard; + } + + @SubscribeEvent + public static void registerShaders(RegisterShadersEvent event) throws IOException { + event.registerShader(new ShaderInstance(event.getResourceProvider(), ParticleStorm.asResource("particle_no_discard"), DefaultVertexFormat.PARTICLE), instance -> particleNoDiscard = instance); + } @SubscribeEvent public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) { @@ -60,22 +84,21 @@ public static void registerRenderers(EntityRenderersEvent.RegisterRenderers even } @SubscribeEvent - public static void clientSetup(FMLClientSetupEvent event) { - event.enqueueWork(() -> { + public static void modConfig$Loading(ModConfigEvent.Loading event) { + if (ParticleStorm.MODID.equals(event.getConfig().getModId())) { PSClientConfigs.onLoad(); - NeoForge.EVENT_BUS.addListener(PSGameClient::tick); - NeoForge.EVENT_BUS.addListener(PSGameClient::renderLevelStage); - }); + } } @SubscribeEvent public static void modConfig$Reloading(ModConfigEvent.Reloading event) { - if (event.getConfig().getModId().equals(ParticleStorm.MODID)) { + if (ParticleStorm.MODID.equals(event.getConfig().getModId())) { PSClientConfigs.onLoad(); } } - private static void tick(ClientTickEvent.Pre event) { + @SubscribeEvent + public static void tick(ClientTickEvent.Pre event) { Minecraft minecraft = Minecraft.getInstance(); LocalPlayer localPlayer = minecraft.player; if (localPlayer == null) { @@ -85,7 +108,8 @@ private static void tick(ClientTickEvent.Pre event) { } } - private static void renderLevelStage(RenderLevelStageEvent event) { + @SubscribeEvent + public static void renderLevelStage(RenderLevelStageEvent event) { if (!PSClientConfigs.showEmitterOutline) return; Minecraft minecraft = Minecraft.getInstance(); if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_PARTICLES && minecraft.getEntityRenderDispatcher().shouldRenderHitBoxes()) { @@ -119,6 +143,13 @@ public static void reload(RegisterClientReloadListenersEvent event) { event.registerReloadListener(LOADER); } + @SubscribeEvent + public static void registerCustomParticleType(RegisterCustomParticleTypeEvent event) { + event.registerWithSprites(ParticleStorm.MOLANG, (emitter, particlePreset, level, x, y, z, sprites) -> + new MolangParticleInstance(particlePreset, level, x, y, z, sprites) + ); + } + private static void registerComponents() { IComponent.register("emitter_local_space", EmitterLocalSpace.CODEC); IComponent.register("emitter_initialization", EmitterInitialization.CODEC); @@ -155,6 +186,8 @@ private static void registerComponents() { IComponent.register("particle_kill_plane", ParticleLifetimeKillPlane.CODEC); IComponent.register("particle_expire_if_in_blocks", ParticleExpireIfInBlocks.CODEC); IComponent.register("particle_expire_if_not_in_blocks", ParticleExpireIfNotInBlocks.CODEC); + + ModLoader.postEvent(new RegisterCustomComponentEvent()); } private static void registerEventNodes() { @@ -165,5 +198,7 @@ private static void registerEventNodes() { IEventNode.register("sound_effect", SoundEffect.CODEC.codec()); IEventNode.register("expression", NodeMolangExp.CODEC); IEventNode.register("log", EventLog.CODEC); + + ModLoader.postEvent(new RegisterCustomEventNodeEvent()); } } diff --git a/src/main/java/org/mesdag/particlestorm/PSModClient.java b/src/main/java/org/mesdag/particlestorm/PSModClient.java deleted file mode 100644 index 4fc4fc1..0000000 --- a/src/main/java/org/mesdag/particlestorm/PSModClient.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.mesdag.particlestorm; - -import net.neoforged.api.distmarker.Dist; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.EventBusSubscriber; -import org.mesdag.particlestorm.api.RegisterCustomParticleTypeEvent; -import org.mesdag.particlestorm.particle.MolangParticleInstance; - -@EventBusSubscriber(modid = ParticleStorm.MODID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) -public final class PSModClient { - @SubscribeEvent - public static void registerCustomParticleType(RegisterCustomParticleTypeEvent event) { - event.registerWithSprites(ParticleStorm.MOLANG, (emitter, particlePreset, level, x, y, z, sprites) -> - new MolangParticleInstance(particlePreset, level, x, y, z, sprites) - ); - } -} diff --git a/src/main/java/org/mesdag/particlestorm/ParticleStorm.java b/src/main/java/org/mesdag/particlestorm/ParticleStorm.java index 6ee9648..77a1809 100644 --- a/src/main/java/org/mesdag/particlestorm/ParticleStorm.java +++ b/src/main/java/org/mesdag/particlestorm/ParticleStorm.java @@ -43,6 +43,7 @@ public final class ParticleStorm { public static final String MODID = "particlestorm"; public static final Logger LOGGER = LoggerFactory.getLogger("ParticleStorm"); public static final boolean DEBUG = Boolean.getBoolean("particlestorm.debug") && LoadingModList.get().getModFileById("geckolib") != null; + public static final boolean IRIS_LOADED = LoadingModList.get().getModFileById("iris") != null; private static final DeferredRegister> REGISTER = DeferredRegister.create(BuiltInRegistries.PARTICLE_TYPE, MODID); public static final DeferredHolder, ParticleType> MOLANG = registerParticleType(REGISTER, "molang"); diff --git a/src/main/java/org/mesdag/particlestorm/api/MolangInstance.java b/src/main/java/org/mesdag/particlestorm/api/MolangInstance.java index 2f0808a..5406291 100644 --- a/src/main/java/org/mesdag/particlestorm/api/MolangInstance.java +++ b/src/main/java/org/mesdag/particlestorm/api/MolangInstance.java @@ -18,13 +18,13 @@ public interface MolangInstance { float tickLifetime(); - double getRandom1(); + float getRandom1(); - double getRandom2(); + float getRandom2(); - double getRandom3(); + float getRandom3(); - double getRandom4(); + float getRandom4(); ResourceLocation getIdentity(); diff --git a/src/main/java/org/mesdag/particlestorm/api/ParticlePresetLoadedEvent.java b/src/main/java/org/mesdag/particlestorm/api/ParticlePresetLoadedEvent.java index a889155..baf5f27 100644 --- a/src/main/java/org/mesdag/particlestorm/api/ParticlePresetLoadedEvent.java +++ b/src/main/java/org/mesdag/particlestorm/api/ParticlePresetLoadedEvent.java @@ -1,15 +1,22 @@ package org.mesdag.particlestorm.api; import net.neoforged.bus.api.Event; +import org.mesdag.particlestorm.data.DefinedParticleEffect; import org.mesdag.particlestorm.particle.ParticlePreset; public class ParticlePresetLoadedEvent extends Event { + private final DefinedParticleEffect effect; private final ParticlePreset preset; - public ParticlePresetLoadedEvent(ParticlePreset preset) { + public ParticlePresetLoadedEvent(DefinedParticleEffect effect, ParticlePreset preset) { + this.effect = effect; this.preset = preset; } + public DefinedParticleEffect getEffect() { + return effect; + } + public ParticlePreset getPreset() { return preset; } diff --git a/src/main/java/org/mesdag/particlestorm/api/RegisterCustomComponentEvent.java b/src/main/java/org/mesdag/particlestorm/api/RegisterCustomComponentEvent.java new file mode 100644 index 0000000..f056c61 --- /dev/null +++ b/src/main/java/org/mesdag/particlestorm/api/RegisterCustomComponentEvent.java @@ -0,0 +1,18 @@ +package org.mesdag.particlestorm.api; + +import com.mojang.serialization.Codec; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; + +public class RegisterCustomComponentEvent extends Event implements IModBusEvent { + public RegisterCustomComponentEvent() {} + + public void register(ResourceLocation id, Codec codec) { + IComponent.register(id, codec); + } + + public void register(String vanillaPath, Codec codec) { + IComponent.register(vanillaPath, codec); + } +} diff --git a/src/main/java/org/mesdag/particlestorm/api/RegisterCustomEventNodeEvent.java b/src/main/java/org/mesdag/particlestorm/api/RegisterCustomEventNodeEvent.java new file mode 100644 index 0000000..3b98950 --- /dev/null +++ b/src/main/java/org/mesdag/particlestorm/api/RegisterCustomEventNodeEvent.java @@ -0,0 +1,13 @@ +package org.mesdag.particlestorm.api; + +import com.mojang.serialization.Codec; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; + +public class RegisterCustomEventNodeEvent extends Event implements IModBusEvent { + public RegisterCustomEventNodeEvent() {} + + public void register(String name, Codec codec) { + IEventNode.register(name, codec); + } +} diff --git a/src/main/java/org/mesdag/particlestorm/api/RegisterCustomMaterialEvent.java b/src/main/java/org/mesdag/particlestorm/api/RegisterCustomMaterialEvent.java new file mode 100644 index 0000000..53dc56a --- /dev/null +++ b/src/main/java/org/mesdag/particlestorm/api/RegisterCustomMaterialEvent.java @@ -0,0 +1,19 @@ +package org.mesdag.particlestorm.api; + +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import org.mesdag.particlestorm.data.description.DescriptionMaterial; + +import java.util.function.Function; + +public class RegisterCustomMaterialEvent extends Event implements IModBusEvent { + private final Function consumer; + + public RegisterCustomMaterialEvent(Function function) { + this.consumer = function; + } + + public DescriptionMaterial register(String name) { + return consumer.apply(name); + } +} diff --git a/src/main/java/org/mesdag/particlestorm/api/RegisterMolangQueriesEvent.java b/src/main/java/org/mesdag/particlestorm/api/RegisterMolangQueriesEvent.java index 9856c80..91faf44 100644 --- a/src/main/java/org/mesdag/particlestorm/api/RegisterMolangQueriesEvent.java +++ b/src/main/java/org/mesdag/particlestorm/api/RegisterMolangQueriesEvent.java @@ -4,16 +4,15 @@ import net.neoforged.fml.event.IModBusEvent; import java.util.function.BiConsumer; -import java.util.function.ToDoubleFunction; public class RegisterMolangQueriesEvent extends Event implements IModBusEvent { - private final BiConsumer> variable; + private final BiConsumer> variable; - public RegisterMolangQueriesEvent(BiConsumer> variable) { + public RegisterMolangQueriesEvent(BiConsumer> variable) { this.variable = variable; } - public void registerVariable(String name, ToDoubleFunction value) { + public void registerVariable(String name, ToFloatFunction value) { variable.accept(name, value); } } diff --git a/src/main/java/org/mesdag/particlestorm/api/ToFloatFunction.java b/src/main/java/org/mesdag/particlestorm/api/ToFloatFunction.java new file mode 100644 index 0000000..523cc13 --- /dev/null +++ b/src/main/java/org/mesdag/particlestorm/api/ToFloatFunction.java @@ -0,0 +1,6 @@ +package org.mesdag.particlestorm.api; + +@FunctionalInterface +public interface ToFloatFunction { + float applyAsFloat(T t); +} diff --git a/src/main/java/org/mesdag/particlestorm/data/MathHelper.java b/src/main/java/org/mesdag/particlestorm/data/MathHelper.java index 1f5f000..e6f2e4a 100644 --- a/src/main/java/org/mesdag/particlestorm/data/MathHelper.java +++ b/src/main/java/org/mesdag/particlestorm/data/MathHelper.java @@ -106,7 +106,11 @@ public static void forCompound(Map table, List toInit, VariableTable vars) { for (VariableAssignment assignment : toInit) { // 重定向,防止因找不到变量而爆栈 - vars.setValue(assignment.variable().name(), assignment.value()); + Variable variable = new Variable(assignment.variable().name(), assignment.value()); + if (!assignment.variable().isMutable()) { + variable.markImmutable(); + } + vars.setValue(variable.name(), variable); } } } diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterInitialization.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterInitialization.java index 06df93e..784fc90 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterInitialization.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterInitialization.java @@ -18,6 +18,10 @@ public record EmitterInitialization(MolangExp creationExpression, MolangExp perU MolangExp.CODEC.fieldOf("per_update_expression").orElse(MolangExp.EMPTY).forGetter(EmitterInitialization::perUpdateExpression) ).apply(instance, EmitterInitialization::new)); + public EmitterInitialization { + creationExpression.markImmutable(); + } + @Override public Codec codec() { return CODEC; @@ -29,13 +33,13 @@ public List getAllMolangExp() { } @Override - public void update(ParticleEmitter entity) { - perUpdateExpression.calculate(entity); + public void update(ParticleEmitter emitter) { + perUpdateExpression.calculate(emitter); } @Override - public void apply(ParticleEmitter entity) { - creationExpression.calculate(entity); + public void apply(ParticleEmitter emitter) { + creationExpression.calculate(emitter); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/description/DescriptionMaterial.java b/src/main/java/org/mesdag/particlestorm/data/description/DescriptionMaterial.java index 8e2c880..400788c 100644 --- a/src/main/java/org/mesdag/particlestorm/data/description/DescriptionMaterial.java +++ b/src/main/java/org/mesdag/particlestorm/data/description/DescriptionMaterial.java @@ -2,26 +2,52 @@ import com.mojang.serialization.Codec; import net.minecraft.util.StringRepresentable; +import net.neoforged.fml.ModLoader; +import org.mesdag.particlestorm.api.RegisterCustomMaterialEvent; -import java.util.Locale; +import java.util.ArrayList; +import java.util.List; -public enum DescriptionMaterial implements StringRepresentable { - TERRAIN_SHEET, - PARTICLE_SHEET_OPAQUE, - PARTICLE_SHEET_TRANSLUCENT, - PARTICLE_SHEET_LIT, - CUSTOM, - NO_RENDER, +public final class DescriptionMaterial implements StringRepresentable { + private static List list = new ArrayList<>(); + private static DescriptionMaterial[] values; - particles_alpha, - particles_blend, - particles_add, - particles_opaque; + public static final DescriptionMaterial + TERRAIN_SHEET = register("terrain_sheet"), + PARTICLE_SHEET_OPAQUE = register("particle_sheet_opaque"), + PARTICLE_SHEET_TRANSLUCENT = register("particle_sheet_translucent"), + PARTICLE_SHEET_LIT = register("particle_sheet_lit"), + CUSTOM = register("custom"), + NO_RENDER = register("no_renderer"), + particles_alpha = register("particles_alpha"), + particles_blend = register("particles_blend"), + particles_add = register("particles_add"), + particles_opaque = register("particles_opaque"); - public static final Codec CODEC = StringRepresentable.fromEnum(DescriptionMaterial::values); + + private static DescriptionMaterial register(String name) { + DescriptionMaterial material = new DescriptionMaterial(name); + list.add(material); + return material; + } + + public static final Codec CODEC = StringRepresentable.fromValues(() -> { + if (values == null) { + ModLoader.postEvent(new RegisterCustomMaterialEvent(DescriptionMaterial::register)); + values = list.toArray(DescriptionMaterial[]::new); + list = null; + } + return values; + }); + + private final String name; + + private DescriptionMaterial(String name) { + this.name = name; + } @Override public String getSerializedName() { - return name().toLowerCase(Locale.ROOT); + return name; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp.java b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp.java index ef325dd..e0ddf2b 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp.java @@ -30,7 +30,7 @@ public boolean initialized() { @Override public float calculate(MolangInstance instance) { if (!initialized()) return 0.0F; - return variable == null ? constant : (float) variable.get(instance); + return variable == null ? constant : variable.get(instance); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp2.java b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp2.java index 6c3a3ec..a469e59 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp2.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp2.java @@ -16,6 +16,11 @@ public float[] calculate(MolangInstance instance) { return new float[]{exp1.calculate(instance), exp2.calculate(instance)}; } + public void markImmutable() { + exp1.markImmutable(); + exp2.markImmutable(); + } + @Deprecated public boolean initialized() { return exp1.initialized() && exp2.initialized(); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp3.java b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp3.java index bbb055e..e6af884 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp3.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExp3.java @@ -19,6 +19,12 @@ public float[] calculate(MolangInstance instance) { return new float[]{exp1.calculate(instance), exp2.calculate(instance), exp3.calculate(instance)}; } + public void markImmutable() { + exp1.markImmutable(); + exp2.markImmutable(); + exp3.markImmutable(); + } + @Override public String toString() { return "FloatMolangExp3{" + diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExpList.java b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExpList.java index 5fe2863..a47d1f8 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExpList.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/FloatMolangExpList.java @@ -37,4 +37,10 @@ public FloatMolangExp getExp(int index) { Objects.checkIndex(index, size); return expressions.get(index); } + + public void markImmutable() { + for (FloatMolangExp exp : expressions) { + exp.markImmutable(); + } + } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/MolangExp.java b/src/main/java/org/mesdag/particlestorm/data/molang/MolangExp.java index 01585c4..e05f804 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/MolangExp.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/MolangExp.java @@ -14,11 +14,12 @@ import java.util.Map; public class MolangExp { - public static final MolangExp EMPTY = Util.make(new MolangExp(""), exp -> exp.variable = new Constant(0.0)); + public static final MolangExp EMPTY = Util.make(new MolangExp(""), exp -> exp.variable = new Constant(0)); public static final Codec CODEC = Codec.STRING.xmap(MolangExp::new, MolangExp::getExpStr); public static final StreamCodec STREAM_CODEC = ByteBufCodecs.STRING_UTF8.map(MolangExp::new, MolangExp::getExpStr); protected final String expStr; protected MathValue variable; + protected boolean immutable; public MolangExp(String expStr) { this.expStr = expStr; @@ -52,12 +53,19 @@ public String getExpStr() { public void compile(MolangParser parser) { if (variable == null && !expStr.isEmpty() && !expStr.isBlank()) { this.variable = parser.compileMolang(expStr); + if (immutable) { + variable.markImmutable(); + } } } public float calculate(MolangInstance instance) { if (!initialized()) return 0.0F; - return (float) variable.get(instance); + return variable.get(instance); + } + + public void markImmutable() { + this.immutable = true; } public MathValue getVariable() { diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/VariableTable.java b/src/main/java/org/mesdag/particlestorm/data/molang/VariableTable.java index 12cae18..3dd179d 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/VariableTable.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/VariableTable.java @@ -2,12 +2,12 @@ import org.jetbrains.annotations.Nullable; import org.mesdag.particlestorm.api.MolangInstance; +import org.mesdag.particlestorm.api.ToFloatFunction; import org.mesdag.particlestorm.data.molang.compiler.value.Variable; import java.util.Hashtable; import java.util.Map; import java.util.function.Function; -import java.util.function.ToDoubleFunction; public class VariableTable { public final Map table; @@ -22,16 +22,16 @@ public VariableTable(@Nullable VariableTable parent) { this(new Hashtable<>(), parent); } - public double getValue(String name, MolangInstance instance) { + public float getValue(String name, MolangInstance instance) { Variable variable = table.get(name); if (variable == null) { - if (parent == null) return 0.0; + if (parent == null) return 0; return parent.getValue(name, instance); } return variable.get(instance); } - public void setValue(String name, ToDoubleFunction function) { + public void setValue(String name, ToFloatFunction function) { Variable variable = table.get(name); if (variable == null) { table.put(name, new Variable(name, function)); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MathValue.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MathValue.java index 18bb721..962cc97 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MathValue.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MathValue.java @@ -1,38 +1,31 @@ package org.mesdag.particlestorm.data.molang.compiler; import org.mesdag.particlestorm.api.MolangInstance; +import org.mesdag.particlestorm.api.ToFloatFunction; -import java.util.function.ToDoubleFunction; +/// Base interface for all computational values in the math system +/// +/// All mathematical objects are an extension of this interface, allowing for an indefinitely-nestable +/// mathematical system that can be accessed via this one access point +public interface MathValue extends ToFloatFunction { + /// Get computed or stored value + float get(MolangInstance instance); -/** - * Base interface for all computational values in the math system - *

- * All mathematical objects are an extension of this interface, allowing for an indefinitely-nestable - * mathematical system that can be accessed via this one access point - */ -public interface MathValue extends ToDoubleFunction { - /** - * Get computed or stored value - */ - double get(MolangInstance instance); + default void set(ToFloatFunction function) {} - default void set(ToDoubleFunction function) {} - - /** - * Return whether this type of MathValue should be considered mutable; its value could change. - *
- * This is used to cache calculated values, optimising computational work - */ + /// Return whether this type of MathValue should be considered mutable; its value could change. + /// + /// This is used to cache calculated values, optimising computational work default boolean isMutable() { return true; } - /** - * Use {@link #get} - */ - @Deprecated() + void markImmutable(); + + /// Use [#get] + @Deprecated @Override - default double applyAsDouble(MolangInstance instance) { + default float applyAsFloat(MolangInstance instance) { return get(instance); } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangParser.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangParser.java index 0a4d414..d9cbf2a 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangParser.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangParser.java @@ -287,7 +287,7 @@ protected MathValue compileSingleValue(Either> symbol) t return new BooleanNegate(compileSingleValue(Either.left(string.substring(1)))); if (isNumeric(string)) - return new Constant(Double.parseDouble(string)); + return new Constant(Float.parseFloat(string)); if (string.startsWith("'") && string.endsWith("'")) return new StringValue(string.substring(1, string.length() - 1)); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java index 1c200fa..6274970 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java @@ -7,12 +7,12 @@ import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.api.RegisterMolangQueriesEvent; +import org.mesdag.particlestorm.api.ToFloatFunction; import org.mesdag.particlestorm.data.molang.compiler.value.Variable; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.ToDoubleFunction; public final class MolangQueries { private static Map UNFROZEN_QUERIES = new ConcurrentHashMap<>(); @@ -36,7 +36,7 @@ static Variable getQueryFor(String name) { return FROZEN_QUERIES.getOrDefault(applyQueryAliases(name), new Variable(name, 0)); } - private static void registerQueryVariable(String name, ToDoubleFunction value) { + private static void registerQueryVariable(String name, ToFloatFunction value) { checkFrozen(); checkUnregistered(name); UNFROZEN_QUERIES.put(name, new Variable(name, value)); @@ -52,14 +52,12 @@ private static void checkFrozen() { if (!FROZEN_QUERIES.isEmpty()) throw new UnsupportedOperationException("Had already frozen!"); } - /** - * Parse a given string formatted with a prefix, swapping out any potential aliases for the defined proper name - * - * @param text The base text to parse - * @param properName The "correct" prefix to apply - * @param aliases The available prefixes to check and replace - * @return The unaliased string, or the original string if no aliases match - */ + /// Parse a given string formatted with a prefix, swapping out any potential aliases for the defined proper name + /// + /// @param text The base text to parse + /// @param properName The "correct" prefix to apply + /// @param aliases The available prefixes to check and replace + /// @return The unaliased string, or the original string if no aliases match public static String applyPrefixAliases(String text, String properName, String... aliases) { for (String alias : aliases) { if (text.startsWith(alias)) @@ -77,23 +75,23 @@ public static String applyQueryAliases(String text) { } private static void setDefaultQueryValues() { - registerQueryVariable("query.cardinal_player_facing", p -> Minecraft.getInstance().player == null ? 0.0 : Minecraft.getInstance().player.getDirection().ordinal()); - registerQueryVariable("query.day", p -> p.getLevel().getGameTime() / 24000d); - registerQueryVariable("query.has_cape", p -> Minecraft.getInstance().player == null ? 0.0 : Minecraft.getInstance().player.getSkin().capeTexture() == null ? 0 : 1); + registerQueryVariable("query.cardinal_player_facing", p -> Minecraft.getInstance().player == null ? 0 : Minecraft.getInstance().player.getDirection().ordinal()); + registerQueryVariable("query.day", p -> p.getLevel().getGameTime() / 24000F); + registerQueryVariable("query.has_cape", p -> Minecraft.getInstance().player == null ? 0 : Minecraft.getInstance().player.getSkin().capeTexture() == null ? 0 : 1); registerQueryVariable("query.is_first_person", p -> Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON ? 1 : 0); registerQueryVariable("query.moon_brightness", p -> p.getLevel().getMoonBrightness()); registerQueryVariable("query.moon_phase", p -> p.getLevel().getMoonPhase()); - registerQueryVariable("query.player_level", p -> Minecraft.getInstance().player == null ? 0.0 : Minecraft.getInstance().player.experienceLevel); + registerQueryVariable("query.player_level", p -> Minecraft.getInstance().player == null ? 0 : Minecraft.getInstance().player.experienceLevel); registerQueryVariable("query.time_of_day", p -> p.getLevel().getDayTime() / 24000f); registerQueryVariable("query.time_stamp", p -> p.getLevel().getGameTime()); registerQueryVariable("query.total_emitter_count", p -> PSGameClient.LOADER.totalEmitterCount()); registerQueryVariable("query.total_particle_count", p -> Minecraft.getInstance().particleEngine.particles.values().stream().mapToInt(Collection::size).sum()); - registerQueryVariable("query.attached_x", p -> p.getAttachedEntity() == null ? 0.0 : p.getAttachedEntity().getX()); - registerQueryVariable("query.attached_y", p -> p.getAttachedEntity() == null ? 0.0 : p.getAttachedEntity().getY()); - registerQueryVariable("query.attached_z", p -> p.getAttachedEntity() == null ? 0.0 : p.getAttachedEntity().getZ()); - registerQueryVariable("query.attached_xo", p -> p.getAttachedEntity() == null ? 0.0 : p.getAttachedEntity().xo); - registerQueryVariable("query.attached_yo", p -> p.getAttachedEntity() == null ? 0.0 : p.getAttachedEntity().yo); - registerQueryVariable("query.attached_zo", p -> p.getAttachedEntity() == null ? 0.0 : p.getAttachedEntity().zo); + registerQueryVariable("query.attached_x", p -> p.getAttachedEntity() == null ? 0 : (float) p.getAttachedEntity().getX()); + registerQueryVariable("query.attached_y", p -> p.getAttachedEntity() == null ? 0 : (float) p.getAttachedEntity().getY()); + registerQueryVariable("query.attached_z", p -> p.getAttachedEntity() == null ? 0 : (float) p.getAttachedEntity().getZ()); + registerQueryVariable("query.attached_xo", p -> p.getAttachedEntity() == null ? 0 : (float) p.getAttachedEntity().xo); + registerQueryVariable("query.attached_yo", p -> p.getAttachedEntity() == null ? 0 : (float) p.getAttachedEntity().yo); + registerQueryVariable("query.attached_zo", p -> p.getAttachedEntity() == null ? 0 : (float) p.getAttachedEntity().zo); ModLoader.postEvent(new RegisterMolangQueriesEvent(MolangQueries::registerQueryVariable)); FROZEN_QUERIES = ImmutableMap.copyOf(UNFROZEN_QUERIES); UNFROZEN_QUERIES = null; diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/Operator.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/Operator.java index 5e4feae..cbbf10a 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/Operator.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/Operator.java @@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.chars.CharSet; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.Util; +import net.minecraft.util.Mth; import java.util.Arrays; import java.util.Map; @@ -11,41 +12,37 @@ import java.util.Optional; import java.util.function.BiFunction; -/** - * Mathematical operator representing a single operation - *

- * Each record should represent a distinct mathematical function for computational purposes, with each function being deterministic and immutable. - */ +/// Mathematical operator representing a single operation +/// +/// Each record should represent a distinct mathematical function for computational purposes, with each function being deterministic and immutable. public record Operator(String symbol, int precedence, Operation operation) implements Comparable { private static final Map OPERATORS = new Object2ObjectOpenHashMap<>(14); private static final CharSet OPERATOR_SYMBOLS = Util.make(new CharOpenHashSet(15), set -> set.addAll(Arrays.asList('?', ':', ','))); private static int LONGEST_OPERATOR; - public static final Operator ADD = register("+", 1, Double::sum); + public static final Operator ADD = register("+", 1, Float::sum); public static final Operator SUB = register("-", 1, (a, b) -> a - b); public static final Operator MUL = register("*", 2, (a, b) -> a * b); public static final Operator DIV = register("/", 2, (a, b) -> b == 0 ? a : a / b); public static final Operator MOD = register("%", 2, (a, b) -> b == 0 ? a : a % b); - public static final Operator POW = register("^", 3, Math::pow); + public static final Operator POW = register("^", 3, (a, b) -> (float) Math.pow(a, b)); public static final Operator AND = register("&&", 5, (a, b) -> a != 0 && b != 0 ? 1 : 0); public static final Operator OR = register("||", 5, (a, b) -> a != 0 || b != 0 ? 1 : 0); public static final Operator LT = register("<", 5, (a, b) -> a < b ? 1 : 0); public static final Operator LTE = register("<=", 5, (a, b) -> a <= b ? 1 : 0); public static final Operator GT = register(">", 5, (a, b) -> a > b ? 1 : 0); public static final Operator GTE = register(">=", 5, (a, b) -> a >= b ? 1 : 0); - public static final Operator EQUAL = register("==", 5, (a, b) -> Math.abs(a - b) < 0.00001 ? 1 : 0); - public static final Operator NOT_EQUAL = register("!=", 5, (a, b) -> Math.abs(a - b) >= 0.00001 ? 1 : 0); + public static final Operator EQUAL = register("==", 5, (a, b) -> Math.abs(a - b) < Mth.EPSILON ? 1 : 0); + public static final Operator NOT_EQUAL = register("!=", 5, (a, b) -> Math.abs(a - b) >= Mth.EPSILON ? 1 : 0); //public static final Operator NULL_COALESCING = register("??", 6, (a, b) -> a == 0 ? b : a); public static final Operator ASSIGN_VARIABLE = register("=", Integer.MAX_VALUE, (a, b) -> 0); - /** - * Instantiate and register a new mathematical operator. - * Note that it should be a functionally distinct operator from other existing operators. - * - * @param symbol The expressed mathematical symbol for this operator - * @param precedence The precedence value for this operator, in relation to other operators - * @param operation The computational function for this operator - */ + /// Instantiate and register a new mathematical operator. + /// Note that it should be a functionally distinct operator from other existing operators. + /// + /// @param symbol The expressed mathematical symbol for this operator + /// @param precedence The precedence value for this operator, in relation to other operators + /// @param operation The computational function for this operator public static Operator register(String symbol, int precedence, Operation operation) { final Operator operator = new Operator(symbol, precedence, operation); @@ -61,43 +58,34 @@ public static Operator register(String symbol, int precedence, Operation operati return operator; } - /** - * @param symbol The mathematical/expression symbol representing an Operator - * @return Whether an Operator has been registered for the given symbol - */ + /// @param symbol The mathematical/expression symbol representing an Operator + /// @return Whether an Operator has been registered for the given symbol public static boolean isOperator(String symbol) { return OPERATORS.containsKey(symbol); } - /** - * @param symbol The mathematical/expression symbol representing an Operator - * @return An {@link Optional} potentially containing the Operator for the given symbol - */ + /// @param symbol The mathematical/expression symbol representing an Operator + /// @return An [Optional] potentially containing the Operator for the given symbol public static Optional getOperatorFor(String symbol) { return Optional.ofNullable(OPERATORS.get(symbol)); } - /** - * Returns the character length of the longest currently registered operator - */ + /// Returns the character length of the longest currently registered operator public static int maxOperatorLength() { return LONGEST_OPERATOR; } - /** - * Returns whether the given character is a part of any registered operators or otherwise acts like an operative separator for expressions - */ + /// Returns whether the given character is a part of any registered operators or otherwise acts like an operative separator for expressions public static boolean isOperativeSymbol(char symbol) { return OPERATOR_SYMBOLS.contains(symbol); } - /** - * Compute the resultant value of the two input values for this operation - * @param argA The first input argument - * @param argB The second input argument - * @return The computed value of the two inputs - */ - public double compute(double argA, double argB) { + /// Compute the resultant value of the two input values for this operation + /// + /// @param argA The first input argument + /// @param argB The second input argument + /// @return The computed value of the two inputs + public float compute(float argA, float argB) { return this.operation.compute(argA, argB); } @@ -106,9 +94,7 @@ public int compareTo(Operator operator) { return Integer.compare(this.precedence, operator.precedence); } - /** - * Determine whether this operator takes mathematical precedence over the other operator - */ + /// Determine whether this operator takes mathematical precedence over the other operator public boolean takesPrecedenceOver(Operator operator) { return compareTo(operator) > 0; } @@ -118,18 +104,14 @@ public int hashCode() { return Objects.hash(this.symbol); } - /** - * Functional interface representing the computational work of an {@link Operator} - */ + /// Functional interface representing the computational work of an [Operator] @FunctionalInterface public interface Operation { - /** - * Unboxed equivalent of {@link BiFunction} for computing the mathematical result of two input arguments - * - * @param argA The first input argument - * @param argB The second input argument - * @return The computed value of the two inputs - */ - double compute(double argA, double argB); + /// Unboxed equivalent of [BiFunction] for computing the mathematical result of two input arguments + /// + /// @param argA The first input argument + /// @param argB The second input argument + /// @return The computed value of the two inputs + float compute(float argA, float argB); } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/MathFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/MathFunction.java index c467383..e1f8d39 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/MathFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/MathFunction.java @@ -5,16 +5,14 @@ import java.util.StringJoiner; -/** - * Computational function wrapping a {@link MathValue} - *

- * Subclasses of this represent mathematical functions to be performed on a pre-defined number of input variables. - *
- * Functions should be deterministic - identical input values should result in identical output values, and values should adhere to the {@link MathValue#isMutable() Mutability} contract of {@link MathValue} for determining result-caching. - */ +/// Computational function wrapping a [MathValue] +/// +/// Subclasses of this represent mathematical functions to be performed on a pre-defined number of input variables. +/// +/// Functions should be deterministic - identical input values should result in identical output values, and values should adhere to the [Mutability][#isMutable()] contract of [MathValue] for determining result-caching. public abstract class MathFunction implements MathValue { - private final boolean isMutable; - private double cachedValue = Double.MIN_VALUE; + private boolean isMutable; + private float cachedValue = Float.MIN_VALUE; protected MathFunction(MathValue... values) { validate(values); @@ -22,33 +20,27 @@ protected MathFunction(MathValue... values) { this.isMutable = isMutable(values); } - /** - * Return the expression name/symbol for this function. - * This is the value that would be seen in a mathematical expression string - */ + /// Return the expression name/symbol for this function. + /// This is the value that would be seen in a mathematical expression string public abstract String getName(); @Override - public final double get(MolangInstance instance) { + public final float get(MolangInstance instance) { if (this.isMutable) return compute(instance); - if (this.cachedValue == Double.MIN_VALUE) + if (this.cachedValue == Float.MIN_VALUE) this.cachedValue = compute(instance); return this.cachedValue; } - /** - * Compute the result of this function from its stored arguments - */ - public abstract double compute(MolangInstance instance); + /// Compute the result of this function from its stored arguments + public abstract float compute(MolangInstance instance); - /** - * @return Whether this function should be considered mutable; the value could change - *
- * This would normally be determined by whether any of the stored args are mutable - */ + /// @return Whether this function should be considered mutable; the value could change + /// + /// This would normally be determined by whether any of the stored args are mutable public boolean isMutable(MathValue... values) { for (MathValue value : values) { if (value.isMutable()) @@ -58,19 +50,18 @@ public boolean isMutable(MathValue... values) { return false; } - /** - * @return The minimum number of args required for this function to be computable - */ + @Override + public void markImmutable() { + this.isMutable = false; + } + + /// @return The minimum number of args required for this function to be computable public abstract int getMinArgs(); - /** - * @return The arguments (in order) stored in this function - */ + /// @return The arguments (in order) stored in this function public abstract MathValue[] getArgs(); - /** - * Validate this function's arguments against its minimum requirements, throwing an exception for invalid argument states - */ + /// Validate this function's arguments against its minimum requirements, throwing an exception for invalid argument states public void validate(MathValue... inputs) throws IllegalArgumentException { final int minArgs = getMinArgs(); @@ -95,15 +86,11 @@ public String toString() { return getName() + joiner; } - /** - * Factory interface for {@link MathFunction}. - * Functionally equivalent to

{@code Function}
but with a more concise user-facing handle - */ + /// Factory interface for [MathFunction]. + /// Functionally equivalent to
`Function`
but with a more concise user-facing handle @FunctionalInterface public interface Factory { - /** - * Instantiate a new {@link MathFunction} for the given input values - */ + /// Instantiate a new [MathFunction] for the given input values T create(MathValue... values); } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ACosFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ACosFunction.java index 147ab5a..bc207de 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ACosFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ACosFunction.java @@ -5,14 +5,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the arc-cosine of the input value angle, with the input angle converted to radians - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the arc-cosine of the input value angle, with the input angle converted to radians public final class ACosFunction extends MathFunction { private final MathValue value; @@ -28,8 +25,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.acos(this.value.get(instance) * (double) Mth.DEG_TO_RAD); + public float compute(MolangInstance instance) { + return (float) Math.acos(value.get(instance) * Mth.DEG_TO_RAD); } @Override @@ -39,6 +36,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[]{this.value}; + return new MathValue[]{value}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ASinFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ASinFunction.java index 99ccab8..a2f4111 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ASinFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ASinFunction.java @@ -5,14 +5,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the arc-sine of the input value angle, with the input angle converted to radians - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the arc-sine of the input value angle, with the input angle converted to radians public final class ASinFunction extends MathFunction { private final MathValue value; @@ -28,8 +25,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.asin(this.value.get(instance) * Mth.DEG_TO_RAD); + public float compute(MolangInstance instance) { + return (float) Math.asin(value.get(instance) * Mth.DEG_TO_RAD); } @Override @@ -39,6 +36,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value}; + return new MathValue[] {value}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ATan2Function.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ATan2Function.java index 82cc996..ab18a28 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ATan2Function.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ATan2Function.java @@ -5,14 +5,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the arc-tangent theta of the input rectangular coordinate values (y,x), with the output converted to degrees - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the arc-tangent theta of the input rectangular coordinate values (y,x), with the output converted to degrees public final class ATan2Function extends MathFunction { private final MathValue y; private final MathValue x; @@ -30,8 +27,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.atan2(this.y.get(instance), this.x.get(instance)) * Mth.RAD_TO_DEG; + public float compute(MolangInstance instance) { + return (float) Mth.atan2(y.get(instance), x.get(instance)) * Mth.RAD_TO_DEG; } @Override @@ -41,6 +38,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.y, this.x}; + return new MathValue[] {y, x}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ATanFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ATanFunction.java index 691f19d..a9f9089 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ATanFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ATanFunction.java @@ -5,14 +5,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the arc-tangent of the input value angle, with the input angle converted to radians - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the arc-tangent of the input value angle, with the input angle converted to radians public final class ATanFunction extends MathFunction { private final MathValue value; @@ -28,8 +25,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.atan(this.value.get(instance) * Mth.DEG_TO_RAD); + public float compute(MolangInstance instance) { + return (float) Math.atan(value.get(instance) * Mth.DEG_TO_RAD); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/AbsFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/AbsFunction.java index 8563329..b98fd9b 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/AbsFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/AbsFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the absolute (non-negative) equivalent of the input value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the absolute (non-negative) equivalent of the input value public final class AbsFunction extends MathFunction { private final MathValue value; @@ -27,7 +24,7 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { + public float compute(MolangInstance instance) { return Math.abs(this.value.get(instance)); } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/CosFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/CosFunction.java index 57e40a0..82f33f1 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/CosFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/CosFunction.java @@ -5,14 +5,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the cosine of the input value angle, with the input angle converted to radians - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the cosine of the input value angle, with the input angle converted to radians public final class CosFunction extends MathFunction { private final MathValue value; @@ -28,8 +25,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Mth.cos((float)this.value.get(instance) * Mth.DEG_TO_RAD); + public float compute(MolangInstance instance) { + return Mth.cos(value.get(instance) * Mth.DEG_TO_RAD); } @Override @@ -39,6 +36,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value}; + return new MathValue[] {value}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ExpFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ExpFunction.java index f080777..e069d04 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ExpFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ExpFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns euler's number raised to the power of the input value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns euler's number raised to the power of the input value public final class ExpFunction extends MathFunction { private final MathValue value; @@ -27,8 +24,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.exp(this.value.get(instance)); + public float compute(MolangInstance instance) { + return (float) Math.exp(value.get(instance)); } @Override @@ -38,6 +35,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value}; + return new MathValue[] {value}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/LogFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/LogFunction.java index 5032a84..00ffc7c 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/LogFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/LogFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the log value (euler base) of the input value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the log value (euler base) of the input value public final class LogFunction extends MathFunction { private final MathValue value; @@ -27,8 +24,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.log(this.value.get(instance)); + public float compute(MolangInstance instance) { + return (float) Math.log(value.get(instance)); } @Override @@ -38,6 +35,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value}; + return new MathValue[] {value}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ModFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ModFunction.java index 86d2e71..a38829b 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ModFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/ModFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the remainder value of the input value when modulo'd by the modulus value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the remainder value of the input value when modulo'd by the modulus value public final class ModFunction extends MathFunction { private final MathValue value; private final MathValue modulus; @@ -29,8 +26,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return this.value.get(instance) % this.modulus.get(instance); + public float compute(MolangInstance instance) { + return value.get(instance) % modulus.get(instance); } @Override @@ -40,6 +37,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value, this.modulus}; + return new MathValue[] {value, modulus}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/PowFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/PowFunction.java index 3e4ae6d..19e56b3 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/PowFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/PowFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the input value raised to the power of the second input value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the input value raised to the power of the second input value public final class PowFunction extends MathFunction { private final MathValue value; private final MathValue power; @@ -29,8 +26,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.pow(this.value.get(instance), this.power.get(instance)); + public float compute(MolangInstance instance) { + return (float) Math.pow(value.get(instance), power.get(instance)); } @Override @@ -40,6 +37,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value, this.power}; + return new MathValue[] {value, power}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/SinFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/SinFunction.java index 4acab8b..0fd1127 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/SinFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/SinFunction.java @@ -5,14 +5,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the sine of the input value angle, with the input angle converted to radians - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the sine of the input value angle, with the input angle converted to radians public final class SinFunction extends MathFunction { private final MathValue value; @@ -28,8 +25,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.sin(this.value.get(instance) * Mth.DEG_TO_RAD); + public float compute(MolangInstance instance) { + return Mth.sin(value.get(instance) * Mth.DEG_TO_RAD); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/SqrtFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/SqrtFunction.java index 31fb543..626ba4e 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/SqrtFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/generic/SqrtFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the square root of the input value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the square root of the input value public final class SqrtFunction extends MathFunction { private final MathValue value; @@ -27,8 +24,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.sqrt(this.value.get(instance)); + public float compute(MolangInstance instance) { + return (float) Math.sqrt(value.get(instance)); } @Override @@ -38,6 +35,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value}; + return new MathValue[] {value}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/ClampFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/ClampFunction.java index 302eccd..257794f 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/ClampFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/ClampFunction.java @@ -5,14 +5,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the first input value if is larger than the second input value and less than the third input value; or else returns the nearest of the second two input values - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the first input value if is larger than the second input value and less than the third input value; or else returns the nearest of the second two input values public final class ClampFunction extends MathFunction { private final MathValue value; private final MathValue min; @@ -32,8 +29,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Mth.clamp(this.value.get(instance), this.min.get(instance), this.max.get(instance)); + public float compute(MolangInstance instance) { + return Mth.clamp(value.get(instance), min.get(instance), max.get(instance)); } @Override @@ -43,6 +40,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value, this.min, this.max}; + return new MathValue[] {value, min, max}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/MaxFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/MaxFunction.java index 7707b1c..f3ad283 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/MaxFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/MaxFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the greater of the two input values - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the greater of the two input values public final class MaxFunction extends MathFunction { private final MathValue valueA; private final MathValue valueB; @@ -29,8 +26,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.max(this.valueA.get(instance), this.valueB.get(instance)); + public float compute(MolangInstance instance) { + return Math.max(valueA.get(instance), valueB.get(instance)); } @Override @@ -40,6 +37,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.valueA, this.valueB}; + return new MathValue[] {valueA, valueB}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/MinFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/MinFunction.java index ac675bc..110d8d8 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/MinFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/limit/MinFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the lesser of the two input values - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the lesser of the two input values public final class MinFunction extends MathFunction { private final MathValue valueA; private final MathValue valueB; @@ -29,8 +26,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.min(this.valueA.get(instance), this.valueB.get(instance)); + public float compute(MolangInstance instance) { + return Math.min(valueA.get(instance), valueB.get(instance)); } @Override @@ -40,6 +37,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.valueA, this.valueB}; + return new MathValue[] {valueA, valueB}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/IsBlockFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/IsBlockFunction.java index 966cce2..dd594a6 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/IsBlockFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/IsBlockFunction.java @@ -36,7 +36,7 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { + public float compute(MolangInstance instance) { BlockState state = instance.getLevel().getBlockState(BlockPos.containing(instance.getPosition())); return either.map(state::is, state::is) ? 1 : 0; } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/PiFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/PiFunction.java index 5a61352..158ee42 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/PiFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/PiFunction.java @@ -1,18 +1,16 @@ package org.mesdag.particlestorm.data.molang.compiler.function.misc; +import net.minecraft.util.Mth; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; import org.mesdag.particlestorm.data.molang.compiler.value.Constant; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns PI - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns PI public final class PiFunction extends MathFunction { public PiFunction(MathValue... values) { super(values); @@ -24,8 +22,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.PI; + public float compute(MolangInstance instance) { + return Mth.PI; } @Override @@ -40,6 +38,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {new Constant(Math.PI)}; + return new MathValue[] {new Constant(Mth.PI)}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/ToDegFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/ToDegFunction.java index c904c4d..a767301 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/ToDegFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/ToDegFunction.java @@ -1,17 +1,15 @@ package org.mesdag.particlestorm.data.molang.compiler.function.misc; +import net.minecraft.util.Mth; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Converts the input value to degrees - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Converts the input value to degrees public final class ToDegFunction extends MathFunction { private final MathValue value; @@ -27,8 +25,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.toDegrees(this.value.get(instance)); + public float compute(MolangInstance instance) { + return value.get(instance) * Mth.RAD_TO_DEG; } @Override @@ -38,6 +36,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value}; + return new MathValue[] {value}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/ToRadFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/ToRadFunction.java index 74cbb1c..4f25426 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/ToRadFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/misc/ToRadFunction.java @@ -1,17 +1,15 @@ package org.mesdag.particlestorm.data.molang.compiler.function.misc; +import net.minecraft.util.Mth; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Converts the input value to radians - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Converts the input value to radians public final class ToRadFunction extends MathFunction { private final MathValue value; @@ -27,8 +25,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.toRadians(this.value.get(instance)); + public float compute(MolangInstance instance) { + return value.get(instance) * Mth.DEG_TO_RAD; } @Override @@ -38,6 +36,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value}; + return new MathValue[] {value}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/DieRollFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/DieRollFunction.java index c208b7c..90eed32 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/DieRollFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/DieRollFunction.java @@ -8,18 +8,15 @@ import java.util.Random; import java.util.concurrent.ThreadLocalRandom; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns a random value based on the input values: - *

    - *
  • Three inputs generates the sum of n (first input) random values between the second (inclusive) and third input (exclusive)
  • - *
  • Four inputs generates the sum of n (first input) random values between the second (inclusive) and third input (exclusive), seeded by the fourth input
  • - *
- */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns a random value based on the input values: +/// +/// - Three inputs generates the sum of _n_ (first input) random values between the second (inclusive) and third input (exclusive) +/// - Four inputs generates the sum of _n_ (first input) random values between the second (inclusive) and third input (exclusive), seeded by the fourth input +/// public final class DieRollFunction extends MathFunction { private final MathValue rolls; private final MathValue min; @@ -45,23 +42,22 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - final int rolls = (int)(Math.floor(this.rolls.get(instance))); - final double min = this.min.get(instance); - final double max = this.max.get(instance); - double sum = 0; + public float compute(MolangInstance instance) { + int rolls = (int) (Math.floor(this.rolls.get(instance))); + float min = this.min.get(instance); + float max = this.max.get(instance); + float sum = 0; Random random; if (this.random != null) { random = this.random; - random.setSeed((long)this.seed.get(instance)); - } - else { + random.setSeed((long) this.seed.get(instance)); + } else { random = ThreadLocalRandom.current(); } for (int i = 0; i < rolls; i++) { - sum += min + random.nextDouble() * (max - min); + sum += min + random.nextFloat() * (max - min); } return sum; @@ -83,8 +79,8 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { if (this.seed != null) - return new MathValue[] {this.rolls, this.min, this.max, this.seed}; + return new MathValue[]{this.rolls, this.min, this.max, this.seed}; - return new MathValue[] {this.rolls, this.min, this.max}; + return new MathValue[]{this.rolls, this.min, this.max}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/DieRollIntegerFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/DieRollIntegerFunction.java index 4170134..0aeb9ac 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/DieRollIntegerFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/DieRollIntegerFunction.java @@ -9,18 +9,15 @@ import java.util.Random; import java.util.concurrent.ThreadLocalRandom; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns a random integer value based on the input values: - *

    - *
  • Three inputs generates the sum of n (first input) random values between the second (inclusive) and third input (inclusive)
  • - *
  • Four inputs generates the sum of n (first input) random values between the second (inclusive) and third input (inclusive), seeded by the fourth input
  • - *
- */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns a random integer value based on the input values: +/// +/// - Three inputs generates the sum of _n_ (first input) random values between the second (inclusive) and third input (inclusive) +/// - Four inputs generates the sum of _n_ (first input) random values between the second (inclusive) and third input (inclusive), seeded by the fourth input +/// public final class DieRollIntegerFunction extends MathFunction { private final MathValue rolls; private final MathValue min; @@ -46,10 +43,10 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - final int rolls = (int)(Math.floor(this.rolls.get(instance))); - final int min = Mth.floor(this.min.get(instance)); - final int max = Mth.ceil(this.max.get(instance)); + public float compute(MolangInstance instance) { + int rolls = (int)(Math.floor(this.rolls.get(instance))); + int min = Mth.floor(this.min.get(instance)); + int max = Mth.ceil(this.max.get(instance)); int sum = 0; Random random; diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/RandomFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/RandomFunction.java index 4d4cb9b..5761e55 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/RandomFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/RandomFunction.java @@ -7,19 +7,16 @@ import java.util.Random; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns a random value based on the input values: - *

    - *
  • A single input generates a value between 0 and that input (exclusive)
  • - *
  • Two inputs generates a random value between the first (inclusive) and second input (exclusive)
  • - *
  • Three inputs generates a random value between the first (inclusive) and second input (exclusive), seeded by the third input
  • - *
- */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns a random value based on the input values: +/// +/// - A single input generates a value between 0 and that input (exclusive) +/// - Two inputs generates a random value between the first (inclusive) and second input (exclusive) +/// - Three inputs generates a random value between the first (inclusive) and second input (exclusive), seeded by the third input +/// public final class RandomFunction extends MathFunction { private final MathValue valueA; @Nullable @@ -44,23 +41,23 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - double result; - double valueA = this.valueA.get(instance); + public float compute(MolangInstance instance) { + float result; + float valueA = this.valueA.get(instance); if (this.random != null) { this.random.setSeed((long)this.seed.get(instance)); - result = this.random.nextDouble(); + result = this.random.nextFloat(); } else { - result = Math.random(); + result = (float) Math.random(); } if (this.valueB != null) { - double valueB = this.valueB.get(instance); - double min = Math.min(valueA, valueB); - double max = Math.max(valueA, valueB); + float valueB = this.valueB.get(instance); + float min = Math.min(valueA, valueB); + float max = Math.max(valueA, valueB); result = min + result * (max - min); } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/RandomIntegerFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/RandomIntegerFunction.java index 2fd8a32..50c22be 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/RandomIntegerFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/random/RandomIntegerFunction.java @@ -8,19 +8,16 @@ import java.util.Random; import java.util.concurrent.ThreadLocalRandom; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns a random integer value based on the input values: - *

    - *
  • A single input generates a value between 0 and that input (exclusive)
  • - *
  • Two inputs generates a random value between the first (inclusive) and second input (inclusive)
  • - *
  • Three inputs generates a random value between the first (inclusive) and second input (inclusive), seeded by the third input
  • - *
- */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns a random integer value based on the input values: +/// +/// - A single input generates a value between 0 and that input (exclusive) +/// - Two inputs generates a random value between the first (inclusive) and second input (inclusive) +/// - Three inputs generates a random value between the first (inclusive) and second input (inclusive), seeded by the third input +/// public final class RandomIntegerFunction extends MathFunction { private final MathValue valueA; @Nullable @@ -45,9 +42,9 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { + public float compute(MolangInstance instance) { int result; - int valueA = (int)Math.round(this.valueA.get(instance)); + int valueA = Math.round(this.valueA.get(instance)); Random random; if (this.random != null) { @@ -59,7 +56,7 @@ public double compute(MolangInstance instance) { } if (this.valueB != null) { - int valueB = (int)Math.round(this.valueB.get(instance)); + int valueB = Math.round(this.valueB.get(instance)); int min = Math.min(valueA, valueB); int max = Math.max(valueA, valueB); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/CeilFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/CeilFunction.java index 9b7a4e6..8cc454e 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/CeilFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/CeilFunction.java @@ -1,17 +1,15 @@ package org.mesdag.particlestorm.data.molang.compiler.function.round; +import net.minecraft.util.Mth; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the smallest value that is greater than or equal to the input value and is equal to an integer - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the smallest value that is greater than or equal to the input value and is equal to an integer public final class CeilFunction extends MathFunction { private final MathValue value; @@ -27,8 +25,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.ceil(this.value.get(instance)); + public float compute(MolangInstance instance) { + return Mth.ceil(this.value.get(instance)); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/FloorFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/FloorFunction.java index d468a62..a1d3fbe 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/FloorFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/FloorFunction.java @@ -1,17 +1,15 @@ package org.mesdag.particlestorm.data.molang.compiler.function.round; +import net.minecraft.util.Mth; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the largest value that is less than or equal to the input value and is equal to an integer - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the largest value that is less than or equal to the input value and is equal to an integer public final class FloorFunction extends MathFunction { private final MathValue value; @@ -27,8 +25,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return Math.floor(this.value.get(instance)); + public float compute(MolangInstance instance) { + return Mth.floor(this.value.get(instance)); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/HermiteBlendFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/HermiteBlendFunction.java index 0266684..56f2ba4 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/HermiteBlendFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/HermiteBlendFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the Hermite> basis 3t^2 - 2t^3 curve interpolation value based on the input value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the Hermite> basis 3t^2 - 2t^3 curve interpolation value based on the input value public final class HermiteBlendFunction extends MathFunction { private final MathValue valueA; @@ -27,8 +24,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - final double value = this.valueA.get(instance); + public float compute(MolangInstance instance) { + float value = this.valueA.get(instance); return (3 * value * value) - (2 * value * value * value); } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/LerpFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/LerpFunction.java index 828150e..706e434 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/LerpFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/LerpFunction.java @@ -5,14 +5,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the first value plus the difference between the first and second input values multiplied by the third input value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the first value plus the difference between the first and second input values multiplied by the third input value public final class LerpFunction extends MathFunction { private final MathValue min; private final MathValue max; @@ -32,7 +29,7 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { + public float compute(MolangInstance instance) { return Mth.lerp(this.delta.get(instance), this.min.get(instance), this.max.get(instance)); } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/LerpRotFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/LerpRotFunction.java index 09cec1f..34c77c4 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/LerpRotFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/LerpRotFunction.java @@ -5,14 +5,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the first value plus the difference between the first and second input values multiplied by the third input value, wrapping the end result as a degrees value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the first value plus the difference between the first and second input values multiplied by the third input value, wrapping the end result as a degrees value public final class LerpRotFunction extends MathFunction { private final MathValue min; private final MathValue max; @@ -32,7 +29,7 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { + public float compute(MolangInstance instance) { return lerpYaw(this.delta.get(instance), this.min.get(instance), this.max.get(instance)); } @@ -46,10 +43,10 @@ public MathValue[] getArgs() { return new MathValue[] {this.min, this.max, this.delta}; } - public static double lerpYaw(double delta, double start, double end) { + public static float lerpYaw(float delta, float start, float end) { start = Mth.wrapDegrees(start); end = Mth.wrapDegrees(end); - double diff = start - end; + float diff = start - end; end = diff > 180 || diff < -180 ? start + Math.copySign(360 - Math.abs(diff), diff) : end; return Mth.lerp(delta, start, end); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/RoundFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/RoundFunction.java index a5fcef5..3ec5759 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/RoundFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/RoundFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the closest integer value to the input value - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the closest integer value to the input value public final class RoundFunction extends MathFunction { private final MathValue value; @@ -27,7 +24,7 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { + public float compute(MolangInstance instance) { return Math.round(this.value.get(instance)); } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/TruncateFunction.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/TruncateFunction.java index 2116fc4..be51211 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/TruncateFunction.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/function/round/TruncateFunction.java @@ -4,14 +4,11 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.function.MathFunction; -/** - * {@link MathFunction} value supplier - * - *

- * Contract: - *
- * Returns the closest value that is equal to the input value or closer to zero, and is equal to an integer - */ +/// [MathFunction] value supplier +/// +/// **Contract:** +/// +/// Returns the closest value that is equal to the input value or closer to zero, and is equal to an integer public final class TruncateFunction extends MathFunction { private final MathValue value; @@ -27,8 +24,8 @@ public String getName() { } @Override - public double compute(MolangInstance instance) { - return (long)this.value.get(instance); + public float compute(MolangInstance instance) { + return (int) this.value.get(instance); } @Override @@ -38,6 +35,6 @@ public int getMinArgs() { @Override public MathValue[] getArgs() { - return new MathValue[] {this.value}; + return new MathValue[]{this.value}; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/BooleanNegate.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/BooleanNegate.java index 2d061c0..ad3ac6e 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/BooleanNegate.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/BooleanNegate.java @@ -3,20 +3,22 @@ import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; -/** - * {@link MathValue} value supplier - * - *

- * Contract: - *
- * Returns 1 if the contained value is equal to 0, otherwise returns 0 - */ +/// [MathValue] value supplier +/// +/// **Contract:** +/// +/// Returns **1** if the contained value is equal to **0**, otherwise returns **0** public record BooleanNegate(MathValue value) implements MathValue { @Override - public double get(MolangInstance instance) { + public float get(MolangInstance instance) { return this.value.get(instance) == 0 ? 1 : 0; } + @Override + public void markImmutable() { + value.markImmutable(); + } + @Override public boolean isMutable() { return this.value.isMutable(); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Calculation.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Calculation.java index c2f31df..28a9244 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Calculation.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Calculation.java @@ -4,43 +4,26 @@ import org.mesdag.particlestorm.data.molang.compiler.MathValue; import org.mesdag.particlestorm.data.molang.compiler.Operator; -/** - * {@link MathValue} value supplier - * - *

- * Contract: - *
- * A computed value of argA and argB defined by the contract of the {@link Operator} - */ -public final class Calculation implements MathValue { - public final Operator operator; - public final MathValue argA; - public final MathValue argB; - public final boolean isMutable; - - private double cachedValue = Double.MIN_VALUE; - - public Calculation(Operator operator, MathValue argA, MathValue argB) { - this.operator = operator; - this.argA = argA; - this.argB = argB; - this.isMutable = this.argA.isMutable() || this.argB.isMutable(); +/// [MathValue] value supplier +/// +/// **Contract:** +/// +/// A computed value of argA and argB defined by the contract of the [Operator] +public record Calculation(Operator operator, MathValue argA, MathValue argB) implements MathValue { + @Override + public float get(MolangInstance instance) { + return this.operator.compute(this.argA.get(instance), this.argB.get(instance)); } @Override - public double get(MolangInstance instance) { - if (this.isMutable) - return this.operator.compute(this.argA.get(instance), this.argB.get(instance)); - - if (this.cachedValue == Double.MIN_VALUE) - this.cachedValue = this.operator.compute(this.argA.get(instance), this.argB.get(instance)); - - return this.cachedValue; + public boolean isMutable() { + return argA.isMutable() || argB.isMutable(); } @Override - public boolean isMutable() { - return this.isMutable; + public void markImmutable() { + argA.markImmutable(); + argB.markImmutable(); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/CompoundValue.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/CompoundValue.java index 186bdd5..899fc3b 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/CompoundValue.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/CompoundValue.java @@ -5,18 +5,15 @@ import java.util.StringJoiner; -/** - * {@link MathValue} value supplier - * - *

- * Contract: - *
- * Contains a collection of sub-expressions that evaluate before returning the last expression, or 0 if no return is defined. - * Sub-expressions have no bearing on the final return with exception for where they may be setting variable values - */ +/// [MathValue] value supplier +/// +/// **Contract:** +/// +/// Contains a collection of sub-expressions that evaluate before returning the last expression, or 0 if no return is defined. +/// Sub-expressions have no bearing on the final return with exception for where they may be setting variable values public record CompoundValue(MathValue[] subValues) implements MathValue { @Override - public double get(MolangInstance instance) { + public float get(MolangInstance instance) { for (int i = 0; i < this.subValues.length - 1; i++) { this.subValues[i].get(instance); } @@ -24,6 +21,13 @@ public double get(MolangInstance instance) { return this.subValues[this.subValues.length - 1].get(instance); } + @Override + public void markImmutable() { + for (MathValue value : subValues) { + value.markImmutable(); + } + } + @Override public String toString() { final StringJoiner joiner = new StringJoiner("; "); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Constant.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Constant.java index f635d21..54ced6b 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Constant.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Constant.java @@ -3,17 +3,14 @@ import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; -/** - * {@link MathValue} value supplier - * - *

- * Contract: - *
- * An immutable double value - */ -public record Constant(double value) implements MathValue { +/// [MathValue] value supplier +/// +/// **Contract:** +/// +/// An immutable double value +public record Constant(float value) implements MathValue { @Override - public double get(MolangInstance instance) { + public float get(MolangInstance instance) { return this.value; } @@ -22,6 +19,9 @@ public boolean isMutable() { return false; } + @Override + public void markImmutable() {} + @Override public String toString() { return String.valueOf(this.value); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Group.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Group.java index b3ea997..0d784d0 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Group.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Group.java @@ -3,20 +3,22 @@ import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; -/** - * {@link MathValue} value supplier - * - *

- * Contract: - *
- * An unaltered return of the stored MathValue - */ +/// [MathValue] value supplier +/// +/// **Contract:** +/// +/// An unaltered return of the stored MathValue public record Group(MathValue contents) implements MathValue { @Override - public double get(MolangInstance instance) { + public float get(MolangInstance instance) { return this.contents.get(instance); } + @Override + public void markImmutable() { + contents.markImmutable(); + } + @Override public boolean isMutable() { return this.contents.isMutable(); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Negative.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Negative.java index 41bf50a..4c15fbe 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Negative.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Negative.java @@ -3,20 +3,22 @@ import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; -/** - * {@link MathValue} value supplier - * - *

- * Contract: - *
- * Negated equivalent of the stored value; returning a positive number if the stored value is negative, or a negative value if the stored value is positive - */ +/// [MathValue] value supplier +/// +/// **Contract:** +/// +/// Negated equivalent of the stored value; returning a positive number if the stored value is negative, or a negative value if the stored value is positive public record Negative(MathValue value) implements MathValue { @Override - public double get(MolangInstance instance) { + public float get(MolangInstance instance) { return -this.value.get(instance); } + @Override + public void markImmutable() { + value.markImmutable(); + } + @Override public boolean isMutable() { return this.value.isMutable(); diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/StringValue.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/StringValue.java index d743da6..074d78c 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/StringValue.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/StringValue.java @@ -5,10 +5,18 @@ public record StringValue(String value) implements MathValue { @Override - public double get(MolangInstance instance) { - return 1; + public float get(MolangInstance instance) { + return value.isEmpty() ? 0 : 1; } + @Override + public boolean isMutable() { + return false; + } + + @Override + public void markImmutable() {} + @Override public String toString() { return value; diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Ternary.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Ternary.java index b6ead36..a696eb7 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Ternary.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Ternary.java @@ -3,19 +3,23 @@ import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; -/** - * {@link MathValue} value supplier - * - *

- * Contract: - *
- * Returns one of two stored values dependent on the result of the stored condition value. - * This returns such that a non-zero result from the condition will return the true stored value, otherwise returning the false stored value - */ +/// [MathValue] value supplier +/// +/// **Contract:** +/// +/// Returns one of two stored values dependent on the result of the stored condition value. +/// This returns such that a non-zero result from the condition will return the **true** stored value, otherwise returning the **false** stored value public record Ternary(MathValue condition, MathValue trueValue, MathValue falseValue) implements MathValue { @Override - public double get(MolangInstance instance) { - return this.condition.get(instance) != 0 ? this.trueValue.get(instance) : this.falseValue.get(instance); + public float get(MolangInstance instance) { + return this.condition.get(instance) == 0 ? this.falseValue.get(instance) : this.trueValue.get(instance); + } + + @Override + public void markImmutable() { + condition.markImmutable(); + trueValue.markImmutable(); + falseValue.markImmutable(); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Variable.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Variable.java index f90d91c..112a408 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Variable.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/Variable.java @@ -2,50 +2,60 @@ import org.mesdag.particlestorm.ParticleStorm; import org.mesdag.particlestorm.api.MolangInstance; +import org.mesdag.particlestorm.api.ToFloatFunction; import org.mesdag.particlestorm.data.molang.compiler.MathValue; -import java.util.function.ToDoubleFunction; - -/** - * {@link MathValue} value supplier - * - *

- * Contract: - *
- * Returns the currently stored value, which may be modified at any given time via {@link #set}. Values may be lazily evaluated to eliminate wasteful usage - */ +/// [MathValue] value supplier +/// +/// **Contract:** +/// +/// Returns the currently stored value, which may be modified at any given time via [#set]. Values may be lazily evaluated to eliminate wasteful usage public final class Variable implements MathValue { private final String name; - private ToDoubleFunction value; - private Double constant; + private ToFloatFunction value; + private Float constant; + private boolean immutable; - public Variable(String name, ToDoubleFunction value) { + public Variable(String name, ToFloatFunction value) { this.name = name; this.value = value; } - public Variable(String name, double value) { + public Variable(String name, float value) { this.name = name; this.constant = value; } @Override - public double get(MolangInstance instance) { + public float get(MolangInstance instance) { try { - if (constant != null) return constant; - return value.applyAsDouble(instance); + float v = constant == null ? value.applyAsFloat(instance) : constant; + if (immutable) { + instance.getVars().setValue(name, new Variable(name, v)); + } + return v; } catch (Exception ex) { ParticleStorm.LOGGER.error("Attempted to use Molang variable for incompatible animatable type ({}). An animation json needs to be fixed", this.name); return 0; } } - public void set(Double value) { + @Override + public boolean isMutable() { + return !immutable; + } + + @Override + public void markImmutable() { + this.immutable = true; + } + + public void set(Float value) { this.constant = value; } @Override - public void set(ToDoubleFunction value) { + public void set(ToFloatFunction value) { this.value = value; } @@ -53,7 +63,20 @@ public String name() { return name; } - public ToDoubleFunction value() { + public ToFloatFunction value() { return value; } + + public Variable copy() { + Variable variable; + if (constant == null) { + variable = new Variable(name, value); + } else { + variable = new Variable(name, constant); + } + if (immutable) { + variable.markImmutable(); + } + return variable; + } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/VariableAssignment.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/VariableAssignment.java index 74e83fa..77b2c4e 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/VariableAssignment.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/value/VariableAssignment.java @@ -3,21 +3,24 @@ import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.compiler.MathValue; -/** - * {@link MathValue} value supplier - * - *

- * Contract: - *
- * Assigns a variable to the given value, then returns 0 - */ +/// [MathValue] value supplier +/// +/// **Contract:** +/// +/// Assigns a variable to the given value, then returns 0 public record VariableAssignment(Variable variable, MathValue value) implements MathValue { @Override - public double get(MolangInstance instance) { + public float get(MolangInstance instance) { variable.set(value.get(instance)); return 0; } + @Override + public void markImmutable() { + variable.markImmutable(); + value.markImmutable(); + } + @Override public String toString() { return variable.name() + "=" + value.toString(); diff --git a/src/main/java/org/mesdag/particlestorm/mixin/EntityMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/EntityMixin.java index eaa3bc2..a3ca38f 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/EntityMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/EntityMixin.java @@ -22,9 +22,9 @@ public abstract class EntityMixin implements IEntity { Hashtable table = new Hashtable<>(); table.put("variable.entity_scale", new Variable("variable.entity_scale", p -> { if (p.getAttachedEntity() instanceof LivingEntity living) { - return living.getAttributeValue(Attributes.SCALE); + return (float) living.getAttributeValue(Attributes.SCALE); } - return 1.0; + return 1; })); this.particlestorm$variableTable = new VariableTable(table, null); } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/ParticleEngineMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/ParticleEngineMixin.java index 55af078..fd793c3 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/ParticleEngineMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/ParticleEngineMixin.java @@ -1,6 +1,9 @@ package org.mesdag.particlestorm.mixin; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.client.particle.ParticleEngine; +import net.minecraft.client.particle.ParticleRenderType; +import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.client.renderer.texture.SpriteLoader; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.resources.ResourceLocation; @@ -20,6 +23,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.Map; +import java.util.function.Supplier; @Mixin(ParticleEngine.class) public abstract class ParticleEngineMixin implements IParticleEngine { @@ -55,4 +59,12 @@ private void registerCustom(CallbackInfo ci) { private SpriteLoader.Preparations cachePreparations(SpriteLoader.Preparations preparations) { return this.particlestorm$preparations = preparations; } + + @ModifyArg(method = "render(Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/Camera;FLnet/minecraft/client/renderer/culling/Frustum;Ljava/util/function/Predicate;)V", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;setShader(Ljava/util/function/Supplier;)V")) + private Supplier modify(Supplier original, @Local ParticleRenderType particlerendertype) { + if (!ParticleStorm.IRIS_LOADED && particlerendertype == PSGameClient.PARTICLE_BLEND) { + return PSGameClient::getParticleNoDiscardShader; + } + return original; + } } diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java index d5d9cd7..a9ae68f 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java @@ -37,20 +37,20 @@ public class MolangParticleInstance extends TextureSheetParticle implements IMol protected Vector3f acceleration = new Vector3f(); protected Vector3f facingDirection = new Vector3f(); protected Vector3f initialSpeed = new Vector3f(); - protected float xRot = 0.0F; - protected float yRot = 0.0F; - protected float xRotO = 0.0F; - protected float yRotO = 0.0F; - protected float rolld = 0.0F; - protected boolean hasCollision = false; - protected float collisionDrag = 0.0F; - protected float coefficientOfRestitution = 0.0F; - protected boolean expireOnContact = false; - - protected final double particleRandom1; - protected final double particleRandom2; - protected final double particleRandom3; - protected final double particleRandom4; + protected float xRot; + protected float yRot; + protected float xRotO; + protected float yRotO; + protected float rolld; + protected boolean hasCollision; + protected float collisionDrag; + protected float coefficientOfRestitution; + protected boolean expireOnContact; + + protected final float particleRandom1; + protected final float particleRandom2; + protected final float particleRandom3; + protected final float particleRandom4; protected List components; protected ParticleEmitter emitter; @@ -60,12 +60,12 @@ public class MolangParticleInstance extends TextureSheetParticle implements IMol protected float[] uvSize; protected float[] uvStep; protected int maxFrame = 1; - protected int currentFrame = 0; + protected int currentFrame; protected float[] UV; protected boolean insideKillPlane; protected ParticleGroup particleGroup; - protected int lastTimeline = 0; + protected int lastTimeline; public MolangParticleInstance(ParticlePreset preset, ClientLevel level, double x, double y, double z, ExtendMutableSpriteSet sprites) { super(level, x, y, z); @@ -78,10 +78,10 @@ public MolangParticleInstance(ParticlePreset preset, ClientLevel level, double x this.scaleV = sprite.contents().height() * preset.invTextureHeight; RandomSource random = level.getRandom(); - this.particleRandom1 = random.nextDouble(); - this.particleRandom2 = random.nextDouble(); - this.particleRandom3 = random.nextDouble(); - this.particleRandom4 = random.nextDouble(); + this.particleRandom1 = random.nextFloat(); + this.particleRandom2 = random.nextFloat(); + this.particleRandom3 = random.nextFloat(); + this.particleRandom4 = random.nextFloat(); } @Override @@ -334,22 +334,22 @@ public Level getLevel() { } @Override - public double getRandom1() { + public float getRandom1() { return particleRandom1; } @Override - public double getRandom2() { + public float getRandom2() { return particleRandom2; } @Override - public double getRandom3() { + public float getRandom3() { return particleRandom3; } @Override - public double getRandom4() { + public float getRandom4() { return particleRandom4; } diff --git a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java index 097aff6..1b3c2ff 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java +++ b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java @@ -48,10 +48,10 @@ public class ParticleEmitter implements MolangInstance { public transient Vector3f inheritedParticleSpeed; public transient boolean isManual; - protected double emitterRandom1; - protected double emitterRandom2; - protected double emitterRandom3; - protected double emitterRandom4; + protected float emitterRandom1; + protected float emitterRandom2; + protected float emitterRandom3; + protected float emitterRandom4; public int id; public transient float invTickRate; @@ -188,10 +188,10 @@ protected void createComponents() { } public synchronized void updateRandoms(RandomSource random) { - this.emitterRandom1 = random.nextDouble(); - this.emitterRandom2 = random.nextDouble(); - this.emitterRandom3 = random.nextDouble(); - this.emitterRandom4 = random.nextDouble(); + this.emitterRandom1 = random.nextFloat(); + this.emitterRandom2 = random.nextFloat(); + this.emitterRandom3 = random.nextFloat(); + this.emitterRandom4 = random.nextFloat(); } public void tick() { @@ -289,10 +289,10 @@ public EmitterPreset getPreset() { public void deserialize(CompoundTag compound) { this.particleId = ResourceLocation.parse(compound.getString("particleId")); this.expression = new MolangExp(compound.getString("expression")); - this.emitterRandom1 = compound.getDouble("emitterRandom1"); - this.emitterRandom2 = compound.getDouble("emitterRandom2"); - this.emitterRandom3 = compound.getDouble("emitterRandom3"); - this.emitterRandom4 = compound.getDouble("emitterRandom4"); + this.emitterRandom1 = compound.getFloat("emitterRandom1"); + this.emitterRandom2 = compound.getFloat("emitterRandom2"); + this.emitterRandom3 = compound.getFloat("emitterRandom3"); + this.emitterRandom4 = compound.getFloat("emitterRandom4"); this.posO = this.pos = new Vec3(compound.getDouble("posX"), compound.getDouble("posY"), compound.getDouble("posZ")); this.rot.set(compound.getFloat("rotX"), compound.getFloat("rotY"), compound.getFloat("rotZ")); } @@ -345,22 +345,22 @@ public float tickLifetime() { } @Override - public double getRandom1() { + public float getRandom1() { return emitterRandom1; } @Override - public double getRandom2() { + public float getRandom2() { return emitterRandom2; } @Override - public double getRandom3() { + public float getRandom3() { return emitterRandom3; } @Override - public double getRandom4() { + public float getRandom4() { return emitterRandom4; } diff --git a/src/main/java/org/mesdag/particlestorm/particle/ParticlePreset.java b/src/main/java/org/mesdag/particlestorm/particle/ParticlePreset.java index 2efaa03..9556f9c 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/ParticlePreset.java +++ b/src/main/java/org/mesdag/particlestorm/particle/ParticlePreset.java @@ -13,6 +13,7 @@ import org.mesdag.particlestorm.data.MathHelper; import org.mesdag.particlestorm.data.component.*; import org.mesdag.particlestorm.data.curve.ParticleCurve; +import org.mesdag.particlestorm.data.description.DescriptionMaterial; import org.mesdag.particlestorm.data.molang.FloatMolangExp; import org.mesdag.particlestorm.data.molang.MolangExp; import org.mesdag.particlestorm.data.molang.VariableTable; @@ -45,15 +46,24 @@ public class ParticlePreset { public ParticlePreset(DefinedParticleEffect effect) { this.effect = effect; - this.renderType = switch (effect.description.parameters().material()) { - case TERRAIN_SHEET -> ParticleRenderType.TERRAIN_SHEET; - case particles_opaque, PARTICLE_SHEET_OPAQUE -> ParticleRenderType.PARTICLE_SHEET_OPAQUE; - case particles_add -> PSGameClient.PARTICLE_ADD; - case particles_blend, PARTICLE_SHEET_TRANSLUCENT -> ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT; - case particles_alpha, PARTICLE_SHEET_LIT -> ParticleRenderType.PARTICLE_SHEET_LIT; - case CUSTOM -> ParticleRenderType.CUSTOM; - default -> ParticleRenderType.NO_RENDER; - }; + DescriptionMaterial material = effect.description.parameters().material(); + if (material == DescriptionMaterial.TERRAIN_SHEET) { + this.renderType = ParticleRenderType.TERRAIN_SHEET; + } else if (material == DescriptionMaterial.particles_opaque || material == DescriptionMaterial.PARTICLE_SHEET_OPAQUE) { + this.renderType = ParticleRenderType.PARTICLE_SHEET_OPAQUE; + } else if (material == DescriptionMaterial.particles_add) { + this.renderType = PSGameClient.PARTICLE_ADD; + } else if (material == DescriptionMaterial.particles_blend) { + this.renderType = PSGameClient.PARTICLE_BLEND; + } else if (material == DescriptionMaterial.PARTICLE_SHEET_TRANSLUCENT) { + this.renderType = ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT; + } else if (material == DescriptionMaterial.particles_alpha || material == DescriptionMaterial.PARTICLE_SHEET_LIT) { + this.renderType = ParticleRenderType.PARTICLE_SHEET_LIT; + } else if (material == DescriptionMaterial.CUSTOM) { + this.renderType = ParticleRenderType.CUSTOM; + } else { + this.renderType = ParticleRenderType.NO_RENDER; + } if (effect.components.get(ParticleAppearanceBillboard.ID) instanceof ParticleAppearanceBillboard component) { this.facingCameraMode = FaceCameraMode.fromComponent(component.faceCameraMode()); this.minSpeedThresholdSqr = Mth.square(component.direction().minSpeedThreshold()); @@ -99,7 +109,7 @@ public ParticlePreset(DefinedParticleEffect effect) { } this.vars = table; this.assignments = toInit; - NeoForge.EVENT_BUS.post(new ParticlePresetLoadedEvent(this)); + NeoForge.EVENT_BUS.post(new ParticlePresetLoadedEvent(effect, this)); } public void setTicket(Class clazz, T value) { diff --git a/src/main/java/org/mesdag/particlestorm/particle/ParticleVariableTable.java b/src/main/java/org/mesdag/particlestorm/particle/ParticleVariableTable.java index 19674ee..20c7b2a 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/ParticleVariableTable.java +++ b/src/main/java/org/mesdag/particlestorm/particle/ParticleVariableTable.java @@ -13,7 +13,7 @@ public ParticleVariableTable(VariableTable preset, VariableTable emitter) { } @Override - public double getValue(String name, MolangInstance instance) { + public float getValue(String name, MolangInstance instance) { Variable variable = table.get(name); if (variable == null) { variable = parent.table.get(name); // 预设表没有父级 diff --git a/src/main/resources/assets/particlestorm/particle_definitions/blend.json b/src/main/resources/assets/particlestorm/particle_definitions/blend.json new file mode 100644 index 0000000..afb83cb --- /dev/null +++ b/src/main/resources/assets/particlestorm/particle_definitions/blend.json @@ -0,0 +1,55 @@ +{ + "format_version": "1.10.0", + "particle_effect": { + "description": { + "identifier": "particlestorm:blend", + "basic_render_parameters": { + "material": "particles_blend", + "texture": "particlestorm:blend" + } + }, + "components": { + "minecraft:emitter_local_space": { + "position": true, + "rotation": false + }, + "minecraft:emitter_rate_steady": { + "spawn_rate": 10, + "max_particles": 100 + }, + "minecraft:emitter_lifetime_looping": { + "active_time": 8 + }, + "minecraft:emitter_shape_point": { + "offset": [0, 0.11, 0], + "direction": ["math.random(-0.5,0.5)", "math.random(0.1,0.5)", "math.random(-0.5,0.5)"] + }, + "minecraft:particle_lifetime_expression": { + "max_lifetime": 4 + }, + "minecraft:particle_initial_speed": "math.random(2,3.5)", + "minecraft:particle_motion_dynamic": { + "linear_acceleration": [0, -0.33, 0], + "linear_drag_coefficient": 0.6 + }, + "minecraft:particle_appearance_billboard": { + "size": ["(v.particle_age / v.particle_lifetime)*0.75+1.5", "(v.particle_age / v.particle_lifetime)*0.75+1.5"], + "facing_camera_mode": "rotate_xyz" + }, + "minecraft:particle_motion_collision": { + "collision_radius": 0.1 + }, + "minecraft:particle_appearance_tinting": { + "color": { + "interpolant": "v.particle_age / v.particle_lifetime", + "gradient": { + "0.0": "#00FFFFFF", + "0.12": "#FFFFFFFF", + "0.59": "#FFFFFFFF", + "1.0": "#00FFFFFF" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/particlestorm/shaders/core/particle_no_discard.fsh b/src/main/resources/assets/particlestorm/shaders/core/particle_no_discard.fsh new file mode 100644 index 0000000..464e37a --- /dev/null +++ b/src/main/resources/assets/particlestorm/shaders/core/particle_no_discard.fsh @@ -0,0 +1,24 @@ +#version 150 + +#moj_import + +uniform sampler2D Sampler0; + +uniform vec4 ColorModulator; +uniform float FogStart; +uniform float FogEnd; +uniform vec4 FogColor; + +in float vertexDistance; +in vec2 texCoord0; +in vec4 vertexColor; + +out vec4 fragColor; + +void main() { + vec4 color = texture(Sampler0, texCoord0) * vertexColor * ColorModulator; +// if (color.a < 0.1) { +// discard; +// } + fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor); +} diff --git a/src/main/resources/assets/particlestorm/shaders/core/particle_no_discard.json b/src/main/resources/assets/particlestorm/shaders/core/particle_no_discard.json new file mode 100644 index 0000000..cdb43e2 --- /dev/null +++ b/src/main/resources/assets/particlestorm/shaders/core/particle_no_discard.json @@ -0,0 +1,17 @@ +{ + "vertex": "minecraft:particle", + "fragment": "particlestorm:particle_no_discard", + "samplers": [ + { "name": "Sampler0" }, + { "name": "Sampler2" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, + { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }, + { "name": "FogShape", "type": "int", "count": 1, "values": [ 0 ] } + ] +} diff --git a/src/main/resources/assets/particlestorm/textures/particle/blend.png b/src/main/resources/assets/particlestorm/textures/particle/blend.png index c18ce562ce79b586712eb291d559e1513aed03c3..fe4e4a327e66df681e18698ee4b212a591bcfc6d 100644 GIT binary patch literal 6141 zcmb_g2{@E(+aAN%6(WT)2HBd$%-F`hlQ2bD$}(nQjAb%Y#y%$ArUl7TXtDHGqNprI zMJie_2ve3&y-3#AzW<}QI==5azU}z`|2dv}p8L6<^IFd9y6)we(^zLa2~l}b001E2 zU~hep_hfE1VL{&Sb5q<~-a~|H?@0pyw!GMEe3{x?6aWB0HrdUC?%}i_gAWUV;Rs>= zL>ME4%0mMH#%2sE4o@M{!T!Vma;OPpvZfXSCKF5`?z&D0C#n@OkZd0nPIQfOcEd+e z@P-74nJL(qf#E3#A<}VRMo4fd4Z|>jeAkQN^*4v%5b$>honiv9*fa?CaKeJE!orDQ zeV7guk3b;6`i3x^fj&yd-*7J&i9qVW5r(`Qia=mc`WTb}_{Rm|nF}Y7FbAz|e;DJv znLq;RbSef8kBEqXMWA3|;Q??RLqnbdBpiu^@(@s3WGEfSfQHgkerm8L((vJADxDk_ z3f|O+^A9^tH-Ye6{fk2g^_SXE+K)K#A_iyRsBj$^Vw2N%AOZgiM?D@M{M|SK4<`l_ zLx`bt8V{@U3rh_QqleJ~!~TWpU!VWZ057&qPQPsYQ(r7X$PT?|TB8)1M!ApU|ng%QZ4$X`%hC_)d4K)E527#$== z*WmApg$0L&^9=L6Lw)zc$qC~S zN~7aK@k9q}69|tpj7%nA&?FoRp`%BDl5}+ap$HNh4K>u$B|wq7XrciD?@!Q25`W5D zhvAQJX7;B%fhVtrAQ4EqBps-}KLG_LlF%e5PLCH2qJciiAB8tGFfjE0Den?a=1nbJ z@c&xR=7ifU35-3N#!FV@j~U@gJocj(Oa_0i0t^nnS=}ZO{AOB-1jvtJ@;`~df5+f2 zwGn|tp3;Al?!REPFcLii7f!SY;6>{{mJ|G6IZwl#`1h`(4Gq!yI{tc4v;l$u#Us)B zP=DlRap4d|eO-TEL+Sja{>$}W<)?$u<*hGU99z9XgJU~S>XxIFIN6JA^`9o*5Gdq=jm-^j;F89H7cx4(-+|Z$NGHyRD}Lus*A7FroHg{Sbo$>=>AuiA*2wmPNn^ zVY~O#?X&Pr#%|#c`49n7c~x32!13cl=G)a%5?%$3%$GvyQIdX8aj7wP$-$|Sg4U#? zOv;AG9qwm%VET%kT<~#eV=vVE+aT<-WT_}kTZ6;1e7_}qg(cwcI3U%vbIg1&&C^sLrwlDt;djds^Boabo;^!fmD%g1Vs=_e31aWcYeB>{dj^Bkr$h#iT6# z)@vzQJv$y0d-OTE`_M}22*sEhCV{Lv|57mb7WnQJLExi?(I``O;KG&Q+{7K}qPF$w z8NwfZs}8YsRjDPmEHz>5k;>-P8p@NPXlvjuN?_YJ*}j*+Q!DMfltk9q&I4C>S%c)Cybqu!<%#)2re>JVQz;q`XWxUvMT+|zJ>G(> zpLXl^)Yj(1W8_x(7G40twd*|0oy$ruiLca$xRdwE%VJ zdUi@3ol>U*KbxxYbH-KkrZhE<^T~u;3#>t}-8b8ZGVvC`XkU1g_|gF($JekOXRuYa zQgnkqcF>LmC&$||wI{^P_6aV>H7*4>?^ZC_XGi?qON#;wPYc-56bV_hyB?8c7XUcsR=er8#`Fz$nHI<K9&_Z8MEqzCHQJk{h=7aKY`VmhfIKX951j@=W>B(>K2kzRP4$ z+D%<%=vyWh9>_CuRy96xkbqBGa^r-m&vL|>{hEnY*Jh?`Ebs84Bs#mQ@|2oa zeHHA6$Es30-xzF3iY;tA15g|1bRUW~`C}%i-=e!gxb_RMd}~E+{lHS_NTSsR^JTlQ zkl_L#cS&luP+9V4adOl)>2h%0B@HvoH6d2o68Ey%?GEyr;pv9f-qNlcs;wa@MO~kE ztudGn2aMQjoH$=W-~C7eCd`v_lWEnB3i3;o{eZ89*T&*A z^~`K5uFIk%>_QZCV&T?C^RmqzpCnGD7Xb(-=aPg3xKbn2URzs} z3eJB%RZ`b#d}_!msYrkw8ez-sO*{hBpY-$^R{Ln@8=>Is<`Z1y_fcPKc4W17%49?P z)3C^*(d^*|F!8yb7N^zN#G*;j-MJgzOyz=xhCAgKAG!{i4HX2vc|8-Q*8c8bG9{(b z!+0{u`^_mR--s$Q^ zdyjR14PjR~v_{hPjIwLrsqG~(dsD+^V}H=N z9z6D@!c^s)P&^(Tb*wD^%3H_6=yh5yD$B7jSQW{DV9As|E0`BP|#p#0c5-6kyl7$jz4)Ulvwv)YuoJSusAxl+YD# zfV(Z+F0v7}xpP%nS9gT6GzE4ybAu{kD{2yan$HfHz2P?!?J-lq zJxHEfqnt0eULi-G0lVMvy0JG~mVAb1&B#zTL-c*@5@><6ajsV%Ba)@cx>;B)DEV+B zR_*PjM^BX1-YH-U+v-8q+s>i;SE@;ohZ=c5FB&Bj`>yo!f(M3SN^Ln_IKX<5e} za{5?tq?t1%qZ&=#wsU~yllr=o!LHDUpN@=NyvEUf%I#gT5nBGLa@|QsMnT-$KEfz; zxieOC=j_Aoffi0~g;O|1V;;28+{W^A8SP#^4sIq)Zxp<6<+_Rgsm@(dY?&QJX z{BFngsNV{`;_tbnMr5`4=SU5tIk;XrDxRO}>H6?m`>lq77<`z|3a2Ar%aHagr|31$BDQ*3?Q>b4|%FrlmdapuJx>zIXmi zSo!1A`q-MpLT!@cy-v_Az}7!S$PtX{p`=|^fZKCkJHjR0(@PV|uHsLWOtXZOK^ihH z5^F%~HY2CK*B;{(&84d;*yJ`{hUe1u`94=YHKypsIhfy?cC2Yzl_YOYM2nx7O5jq$ zAD0R68Sijwc14Zf1&oNTEA2daed+vK+!kp~FOMR!`)eShOFA>?(4q_ynk^U|w96QU zbswRYCUadJX9zW|`w|@$T4cjPR zH$U2Nm|G@1R0deJ_<%}~FQS-_Om@8;;PkoXt?%0zglUYhw5H9n&gDeIR|zclkPXY> z+fS)VVhqpsc&2BCl83$^VA(6rGoe9n=PD$yvTk~Z@MyIWRGTuEj~|VVt`L}5Nr;1O zsI9E0<71MooZ7Uk-U06E@^x&GR+vxci)#g!fv@*fjUv&Kj|bOfX*q|3upX?S$LV0{ z7`HD6pj@pyO7zf*+%v`P%yX&i^PohT0+Mi@3Ez&zX#>vNxn#v|+rgE!r1sB&ZOhNk z#&Yjhh#EP9mTK-Bv8;5=XJ)Aqa(O$MH9&BRgz!hn_DGtdv^^epRXuv2e)OmZsC0YX zWH;Y}my2?o@}86%2~jD^7wym$k~AqWjbFf_?#;EVAYE{W%dR7Mkki8bi#lK5RY#>2 z2G^Z5Z*fl&n*}L>PRJQH==L{$iE#|PyH*z?pz(9etzCT>wT~BR+fcwCd&;m7ghob>}TLc939@Ed4Q!s+csflew)~ z>UYus^AXDhbB>c)Np0myq)L-z$|Ej6;CV5%R~nl>=YL?)%l5{=+~P>@C=6L#{{}}- z^R7$FeAVV%wVpX$A%aa;2COxK zA*^}B!5ZPhIA3^WgF>$okw321gvOsuhG(zJH~YT8lpGRKEl0R&DbRZQfUR7AZ_9=d z-hunp*jQw|-hzfr6FR!&wXfSs!W@(dxi5HNPkzp1BVj_>QK8Up?^*X4UxE~4zv{?^ zsQ~DFMwuSp(YKB;KKAK4e0i#9mt0bHE9K&08`j+T-fH;o7mNKvfPjVi=KBKk$&i=` z{`2Fd13NB2q@Uhoeu$}v|LtD)YOz)VUP>h56_jv{C3J3gg!mtW6T^kEAJ)BGeWRO7 z-b?c_s|(WRn^TSzn=@=BEgWCBEd>pI`P@10t$0KQa6UlfOq15srQm*S2y@NxEV5wT zlqA?POGnay9o zm`~XvfQ-ToFM{Q{?rVD<7QcM3&0H!ie~-$g@Vi;hK4~pF31G!L$7Fv9Y&QichF0C& rkTBZgSG*C7YqWeXGWF@-wM8@9Wx+$RtL;{sf6zPFI9uPf^ke-G9Y~$L literal 498 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0f$LMK~y+TrISl5 zgkczlhsgQZP{=ulB2rip7E?;Gv-0CC$%>T)3R^@D3zS2WoN_iHT-RqtBV*;RtGn;} z=DpwNecoq6|5ZxN|5V@LN+y;A(Z$)$$KYjxk_ND-9IJpDD1=mS@g?jKbc_f35x~M~ ztPP4G3`JnT2Ux`|Y{MKOhj@@2h{g5TF!VqZ82$`rF$}4K7VvwZ?*P;dmaQyt6=q-x zCc&Mbz>o+V_Tyhi;Gq{YAPe>>Z^HM9;x}E`D0ILZY{GcFf)XfzqhyO?+Jkkt0(;X4 z9jPQ+wu|540g{T~8*Cc(;T|#|Yz0x!?XuVRrwDNa`*RJhdv&2#^8yAmQ_&cnRD2u2 z8Xz4m;56RE2sQ*|;Qe}5OLce*cNS`)9qJ$rHeeziP>PK}D_GSXI0N>@A7_Jo$_Kw6 zzye2_^TD&7`vR6>7hLQ#I+^aM=Yfq}gLznjU!&|CRD#12za6`66*lk$w%E`s4fG=* oQH14UmTbcu9RH2ZK!QS{PgxXkwRC4O`2YX_07*qoM6N<$f^b>R761SM diff --git a/src/main/resources/assets/particlestorm/textures/particle/missing.png b/src/main/resources/assets/particlestorm/textures/particle/missing.png deleted file mode 100644 index a374dbfe653f02590b4639950494c30a630b6b14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!8A`7#}Etuy^{>N4j6E_`mUdF^WtxLjpVfR z3CpJ~x*x>(Qd<7W$B8D!#!(MGEIMV+nELS*8{3qpUoTD;2oQT!Trro;Sz+;cMrDHu zYaO=e{%4RCv~*%Lk>A-Zu;P>Dgta${k8quC6n>nf-qn<|P7i1!gQu&X%Q~loCIJ82 BM;rhE diff --git a/src/main/resources/assets/particlestorm/textures/particle/test.png b/src/main/resources/assets/particlestorm/textures/particle/test.png deleted file mode 100644 index b06356802da58efc0ce3b37f52d45f04af1c7818..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^9ze{&!3HFww(ishQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XB4ude`@%$AjK*777E{-7{-fySu2~{B&&Vrcy|B2azB6dZ+*STM^<0! zi+=HG(^AP}*Yz0xF8Ck3pJnQeMUt|9Gu~fowtRGBje6dC2ATd1(G|-1j#eukh)kHz z#}V`KPF>Lx;RnB543$-M6uMYf%h<+DVoblVzx>XZ0u#smX@{>%iFp0L`{XaPpnuGS zkG(o=S7yiTeR835wr|@$H@>LAqExo$1)9olMZ5S{G)sr`EN5PC@b~8_?KAnm*V;17 Xp0MLlOQMG|(4!2Vu6{1-oD!M Date: Sat, 16 May 2026 13:04:37 +0800 Subject: [PATCH 08/13] =?UTF-8?q?=E4=BC=98=E5=8C=96IntAllocator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../mesdag/particlestorm/PSGameClient.java | 13 ++++++---- .../mesdag/particlestorm/ParticleStorm.java | 1 + .../particlestorm/api/IntAllocator.java | 25 ++++++++----------- .../data/component/EmitterLocalSpace.java | 6 ++--- .../component/ParticleMotionParametric.java | 10 +++++--- .../particle/MolangParticleInstance.java | 22 ++++++++++------ .../particle/MolangParticleLoader.java | 4 +++ 8 files changed, 47 insertions(+), 36 deletions(-) diff --git a/gradle.properties b/gradle.properties index 6e3a928..e1fe495 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.2.0 +mod_version=1.2.1 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/org/mesdag/particlestorm/PSGameClient.java b/src/main/java/org/mesdag/particlestorm/PSGameClient.java index f111377..772b49a 100644 --- a/src/main/java/org/mesdag/particlestorm/PSGameClient.java +++ b/src/main/java/org/mesdag/particlestorm/PSGameClient.java @@ -97,14 +97,17 @@ public static void registerRenderers(EntityRenderersEvent.RegisterRenderers even } } + @SubscribeEvent + public static void clientNetwork$LoggingOut(ClientPlayerNetworkEvent.LoggingOut event) { + LOADER.removeAll(); + } + @SubscribeEvent public static void tick(ClientTickEvent.Pre event) { Minecraft minecraft = Minecraft.getInstance(); - LocalPlayer localPlayer = minecraft.player; - if (localPlayer == null) { - LOADER.removeAll(); - } else if (!minecraft.isPaused() && localPlayer.level().tickRateManager().runsNormally()) { - LOADER.tick(localPlayer); + LocalPlayer player = minecraft.player; + if (player != null && !minecraft.isPaused() && player.clientLevel.tickRateManager().runsNormally()) { + LOADER.tick(player); } } diff --git a/src/main/java/org/mesdag/particlestorm/ParticleStorm.java b/src/main/java/org/mesdag/particlestorm/ParticleStorm.java index 77a1809..fe55c0a 100644 --- a/src/main/java/org/mesdag/particlestorm/ParticleStorm.java +++ b/src/main/java/org/mesdag/particlestorm/ParticleStorm.java @@ -43,6 +43,7 @@ public final class ParticleStorm { public static final String MODID = "particlestorm"; public static final Logger LOGGER = LoggerFactory.getLogger("ParticleStorm"); public static final boolean DEBUG = Boolean.getBoolean("particlestorm.debug") && LoadingModList.get().getModFileById("geckolib") != null; + public static final boolean SODIUM_LOADED = LoadingModList.get().getModFileById("sodium") != null; public static final boolean IRIS_LOADED = LoadingModList.get().getModFileById("iris") != null; private static final DeferredRegister> REGISTER = DeferredRegister.create(BuiltInRegistries.PARTICLE_TYPE, MODID); diff --git a/src/main/java/org/mesdag/particlestorm/api/IntAllocator.java b/src/main/java/org/mesdag/particlestorm/api/IntAllocator.java index a4b32f5..1b374f7 100644 --- a/src/main/java/org/mesdag/particlestorm/api/IntAllocator.java +++ b/src/main/java/org/mesdag/particlestorm/api/IntAllocator.java @@ -1,29 +1,24 @@ package org.mesdag.particlestorm.api; +import it.unimi.dsi.fastutil.ints.IntHeapPriorityQueue; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntPriorityQueue; +import it.unimi.dsi.fastutil.ints.IntSet; import org.mesdag.particlestorm.ParticleStorm; -import java.util.HashSet; -import java.util.PriorityQueue; -import java.util.Set; - public class IntAllocator { - private final PriorityQueue availableIds; - private final Set usedIds; + private final IntPriorityQueue availableIds; + private final IntSet usedIds; private int nextId; public IntAllocator() { - this.availableIds = new PriorityQueue<>(); - this.usedIds = new HashSet<>(); + this.availableIds = new IntHeapPriorityQueue(); + this.usedIds = new IntOpenHashSet(); this.nextId = 0; } public int allocate() { - int id; - if (availableIds.isEmpty()) { - id = nextId++; - } else { - id = availableIds.poll(); - } + int id = availableIds.isEmpty() ? nextId++ : availableIds.dequeueInt(); usedIds.add(id); return id; } @@ -31,7 +26,7 @@ public int allocate() { public void release(int id) { if (usedIds.contains(id)) { usedIds.remove(id); - availableIds.offer(id); + availableIds.enqueue(id); } else { ParticleStorm.LOGGER.warn("ID {} is not currently allocated.", id); } diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLocalSpace.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLocalSpace.java index d5ff702..2dac9f5 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLocalSpace.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLocalSpace.java @@ -22,9 +22,9 @@ /// `velocity` will add the emitter's velocity to the initial particle velocity. public record EmitterLocalSpace(boolean position, boolean rotation, boolean velocity) implements IEmitterComponent { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.BOOL.fieldOf("position").orElse(false).forGetter(EmitterLocalSpace::position), - Codec.BOOL.fieldOf("rotation").orElse(false).forGetter(EmitterLocalSpace::rotation), - Codec.BOOL.fieldOf("velocity").orElse(false).forGetter(EmitterLocalSpace::velocity) + Codec.BOOL.lenientOptionalFieldOf("position", false).forGetter(EmitterLocalSpace::position), + Codec.BOOL.lenientOptionalFieldOf("rotation", false).forGetter(EmitterLocalSpace::rotation), + Codec.BOOL.lenientOptionalFieldOf("velocity", false).forGetter(EmitterLocalSpace::velocity) ).apply(instance, EmitterLocalSpace::new)); public EmitterLocalSpace(boolean position, boolean rotation, boolean velocity) { diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionParametric.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionParametric.java index e725700..3eb37f3 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionParametric.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionParametric.java @@ -2,6 +2,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.world.phys.Vec3; import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.IParticleComponent; import org.mesdag.particlestorm.data.molang.FloatMolangExp; @@ -25,9 +26,9 @@ /// Evaluated every frame public record ParticleMotionParametric(FloatMolangExp3 relativePosition, FloatMolangExp3 direction, FloatMolangExp rotation) implements IParticleComponent { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - FloatMolangExp3.CODEC.fieldOf("relative_position").orElse(FloatMolangExp3.ZERO).forGetter(ParticleMotionParametric::relativePosition), - FloatMolangExp3.CODEC.fieldOf("direction").orElse(FloatMolangExp3.ZERO).forGetter(ParticleMotionParametric::direction), - FloatMolangExp.CODEC.fieldOf("rotation").orElse(FloatMolangExp.ZERO).forGetter(ParticleMotionParametric::rotation) + FloatMolangExp3.CODEC.lenientOptionalFieldOf("relative_position", FloatMolangExp3.ZERO).forGetter(ParticleMotionParametric::relativePosition), + FloatMolangExp3.CODEC.lenientOptionalFieldOf("direction", FloatMolangExp3.ZERO).forGetter(ParticleMotionParametric::direction), + FloatMolangExp.CODEC.lenientOptionalFieldOf("rotation", FloatMolangExp.ZERO).forGetter(ParticleMotionParametric::rotation) ).apply(instance, ParticleMotionParametric::new)); @Override @@ -46,7 +47,8 @@ public List getAllMolangExp() { @Override public void update(IMolangParticleInstance instance) { float[] pos = relativePosition.calculate(instance); - instance.moveDirectly(pos[0], pos[1], pos[2]); + Vec3 position = instance.getEmitter().getPosition(); + instance.self().setPos(position.x + pos[0], position.y + pos[1], position.z + pos[2]); if (direction != FloatMolangExp3.ZERO) { float[] dir = direction.calculate(instance); instance.self().setParticleSpeed(dir[0], dir[1], dir[2]); diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java index a9ae68f..894ca96 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java @@ -11,10 +11,11 @@ import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; -import net.neoforged.fml.loading.LoadingModList; import org.joml.Quaternionf; import org.joml.Vector3f; +import org.mesdag.particlestorm.ParticleStorm; import org.mesdag.particlestorm.api.IEventNode; import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.IParticleComponent; @@ -26,9 +27,6 @@ import java.util.Optional; public class MolangParticleInstance extends TextureSheetParticle implements IMolangParticleInstance { - public static final int FULL_LIGHT = 0xF000F0; - private static final boolean isSodiumLoaded = LoadingModList.get().getModFileById("sodium") != null; - protected final ParticlePreset preset; protected ParticleVariableTable vars; protected final float originX; @@ -390,7 +388,7 @@ public void tick() { } } - private static final Quaternionf quaternionf = new Quaternionf(); + protected static final Quaternionf quaternionf = new Quaternionf(); @Override public void render(VertexConsumer buffer, Camera camera, float partialTicks) { @@ -404,7 +402,7 @@ public void render(VertexConsumer buffer, Camera camera, float partialTicks) { @Override protected void renderRotatedQuad(VertexConsumer buffer, Quaternionf quaternion, float x, float y, float z, float partialTicks) { - if (isSodiumLoaded) { + if (ParticleStorm.SODIUM_LOADED) { float f1 = getU0(); float f2 = getU1(); float f3 = getV0(); @@ -419,12 +417,20 @@ protected void renderRotatedQuad(VertexConsumer buffer, Quaternionf quaternion, } } + protected static final Vector3f vector3f = new Vector3f(); + @Override protected void renderVertex(VertexConsumer buffer, Quaternionf quaternion, float x, float y, float z, float xOffset, float yOffset, float quadSize, float u, float v, int packedLight) { - Vector3f vector3f = new Vector3f(xOffset * billboardSize[0], yOffset * billboardSize[1], 0.0F).rotate(quaternion).add(x, y, z); + vector3f.set(xOffset * billboardSize[0], yOffset * billboardSize[1], 0.0F).rotate(quaternion).add(x, y, z); buffer.addVertex(vector3f.x(), vector3f.y(), vector3f.z()).setUv(u, v).setColor(rCol, gCol, bCol, alpha).setLight(packedLight); } + @Override + public AABB getRenderBoundingBox(float partialTicks) { + float size = Math.max(billboardSize[0], billboardSize[1]); + return new AABB(x - size, y - size, z - size, x + size, y + size, z + size); + } + @Override public void move(double x, double y, double z) { if (stoppedByCollision) return; @@ -497,7 +503,7 @@ public FaceCameraMode getFacingCameraMode() { @Override protected int getLightColor(float partialTick) { - return preset.environmentLighting ? super.getLightColor(partialTick) : FULL_LIGHT; + return preset.environmentLighting ? super.getLightColor(partialTick) : 0xF000F0; } @Override diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java index aa990dc..423b4ad 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java @@ -135,6 +135,10 @@ public void addEmitter(ParticleEmitter emitter, boolean sync) { if (sync) EmitterSynchronizePacket.syncToServer(emitter); } + public void addEmitter(ParticleEmitter emitter) { + addEmitter(emitter, false); + } + public boolean addTrackedEmitter(Entity entity, ResourceLocation particleId) { EvictingQueue queue = tracker.computeIfAbsent(entity, e -> EvictingQueue.create(16)); if (!queue.isEmpty() && queue.stream().anyMatch(emitter -> particleId.equals(emitter.particleId))) return false; From 4d2924584185f3e2e72c43066639968161b8a578 Mon Sep 17 00:00:00 2001 From: westernat Date: Sun, 17 May 2026 13:26:24 +0800 Subject: [PATCH 09/13] =?UTF-8?q?=E5=8F=91=E5=B0=84=E5=99=A8=E5=8F=AF?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E9=9A=90=E8=97=8F=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../mesdag/particlestorm/PSGameClient.java | 1 + .../data/component/EmitterLifetimeEvents.java | 13 ++- .../particle/ParticleEmitter.java | 98 +++++++++++-------- 4 files changed, 66 insertions(+), 48 deletions(-) diff --git a/gradle.properties b/gradle.properties index e1fe495..f038542 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.2.1 +mod_version=1.2.2 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/org/mesdag/particlestorm/PSGameClient.java b/src/main/java/org/mesdag/particlestorm/PSGameClient.java index 772b49a..5028d4d 100644 --- a/src/main/java/org/mesdag/particlestorm/PSGameClient.java +++ b/src/main/java/org/mesdag/particlestorm/PSGameClient.java @@ -120,6 +120,7 @@ public static void renderLevelStage(RenderLevelStageEvent event) { PoseStack poseStack = event.getPoseStack(); MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); for (ParticleEmitter emitter : LOADER.getEmitters()) { + if (emitter.hideOutline) continue; double x = Mth.lerp(partialTicks, emitter.posO.x, emitter.pos.x); double y = Mth.lerp(partialTicks, emitter.posO.y, emitter.pos.y); double z = Mth.lerp(partialTicks, emitter.posO.z, emitter.pos.z); diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetimeEvents.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetimeEvents.java index a6a8967..0ea5ec1 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetimeEvents.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetimeEvents.java @@ -105,11 +105,14 @@ public void update(ParticleEmitter emitter) { @Override public void apply(ParticleEmitter emitter) { - emitter.children.removeIf(child -> { - child.parent = null; - child.remove(); - return true; - }); + List children = emitter.getChildren(false); + if (children != null) { + children.removeIf(child -> { + child.parent = null; + child.remove(); + return true; + }); + } executes(emitter, creationEvent); emitter.cachedLooping = new float[loopingTravelDistanceEvents.size()]; } diff --git a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java index 1b3c2ff..108784d 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java +++ b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java @@ -10,6 +10,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; import org.mesdag.particlestorm.PSGameClient; @@ -44,7 +45,7 @@ public class ParticleEmitter implements MolangInstance { protected transient List components; public transient ParticleEmitter parent; public transient @Nullable Runnable afterParentInit; - public transient final List children = new ArrayList<>(); + protected transient @Nullable List children; public transient Vector3f inheritedParticleSpeed; public transient boolean isManual; @@ -55,28 +56,29 @@ public class ParticleEmitter implements MolangInstance { public int id; public transient float invTickRate; - public transient int age = 0; - public transient int lifetime = 0; + public transient int age; + public transient int lifetime; public transient boolean active = true; - public transient int loopingTime = 0; - public transient int activeTime = 0; - public transient int fullLoopTime = 0; + public transient int loopingTime; + public transient int activeTime; + public transient int fullLoopTime; public transient MutableParticleGroup particleGroup; public transient int spawnDuration = 1; - public transient int spawnRate = 0; - public transient boolean spawned = false; + public transient int spawnRate; + public transient boolean spawned; protected transient Entity attached; public transient BlockEntity attachedBlock; - public transient int lastTimeline = 0; - public transient float moveDist = 0.0F; - public transient float moveDistO = 0.0F; - public transient int lastTravelDist = 0; + public transient int lastTimeline; + public transient float moveDist; + public transient float moveDistO; + public transient int lastTravelDist; public transient float[] cachedLooping; public transient final Level level; public Vec3 pos; public Vec3 posO = Vec3.ZERO; public Vector3f rot = new Vector3f(); + public boolean hideOutline; private transient boolean removed = false; public ParticleEmitter(Level level, Vec3 pos, ResourceLocation particleId, MolangExp expression) { @@ -258,19 +260,29 @@ public void remove() { this.removed = true; } + @Contract(value = "true -> !null", pure = true) + public @Nullable List getChildren(boolean create) { + if (create && children == null) { + this.children = new ArrayList<>(); + } + return children; + } + public void onRemove() { - children.removeIf(child -> { - child.parent = null; - child.remove(); - return true; - }); + if (children != null) { + children.removeIf(child -> { + child.parent = null; + child.remove(); + return true; + }); + } if (preset != null && preset.lifetimeEvents != null) { preset.lifetimeEvents.onExpiration(this); } } public void addParent(ParticleEmitter parent) { - parent.children.add(this); + parent.getChildren(true).add(this); this.parent = parent; } @@ -286,30 +298,32 @@ public EmitterPreset getPreset() { return preset; } - public void deserialize(CompoundTag compound) { - this.particleId = ResourceLocation.parse(compound.getString("particleId")); - this.expression = new MolangExp(compound.getString("expression")); - this.emitterRandom1 = compound.getFloat("emitterRandom1"); - this.emitterRandom2 = compound.getFloat("emitterRandom2"); - this.emitterRandom3 = compound.getFloat("emitterRandom3"); - this.emitterRandom4 = compound.getFloat("emitterRandom4"); - this.posO = this.pos = new Vec3(compound.getDouble("posX"), compound.getDouble("posY"), compound.getDouble("posZ")); - this.rot.set(compound.getFloat("rotX"), compound.getFloat("rotY"), compound.getFloat("rotZ")); - } - - public void serialize(CompoundTag compound) { - compound.putString("particleId", particleId.toString()); - compound.putString("expression", expression.getExpStr()); - compound.putDouble("emitterRandom1", emitterRandom1); - compound.putDouble("emitterRandom2", emitterRandom2); - compound.putDouble("emitterRandom3", emitterRandom3); - compound.putDouble("emitterRandom4", emitterRandom4); - compound.putDouble("posX", pos.x); - compound.putDouble("posY", pos.y); - compound.putDouble("posZ", pos.z); - compound.putFloat("rotX", rot.x); - compound.putFloat("rotY", rot.y); - compound.putFloat("rotZ", rot.z); + public void deserialize(CompoundTag tag) { + this.particleId = ResourceLocation.parse(tag.getString("particleId")); + this.expression = new MolangExp(tag.getString("expression")); + this.emitterRandom1 = tag.getFloat("emitterRandom1"); + this.emitterRandom2 = tag.getFloat("emitterRandom2"); + this.emitterRandom3 = tag.getFloat("emitterRandom3"); + this.emitterRandom4 = tag.getFloat("emitterRandom4"); + this.posO = this.pos = new Vec3(tag.getDouble("posX"), tag.getDouble("posY"), tag.getDouble("posZ")); + this.rot.set(tag.getFloat("rotX"), tag.getFloat("rotY"), tag.getFloat("rotZ")); + this.hideOutline = tag.getBoolean("hideOutline"); + } + + public void serialize(CompoundTag tag) { + tag.putString("particleId", particleId.toString()); + tag.putString("expression", expression.getExpStr()); + tag.putDouble("emitterRandom1", emitterRandom1); + tag.putDouble("emitterRandom2", emitterRandom2); + tag.putDouble("emitterRandom3", emitterRandom3); + tag.putDouble("emitterRandom4", emitterRandom4); + tag.putDouble("posX", pos.x); + tag.putDouble("posY", pos.y); + tag.putDouble("posZ", pos.z); + tag.putFloat("rotX", rot.x); + tag.putFloat("rotY", rot.y); + tag.putFloat("rotZ", rot.z); + tag.putBoolean("hideOutline", hideOutline); } public double getX() { From 3740c902ed64de09e9d052aa4ec133f4a00ae1b4 Mon Sep 17 00:00:00 2001 From: westernat Date: Fri, 22 May 2026 14:20:23 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=95=B0=E4=B8=AAbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../mesdag/particlestorm/PSGameClient.java | 8 ++-- .../particlestorm/api/IEmitterComponent.java | 4 +- .../data/component/EmitterLifetime.java | 11 +++++ .../data/component/EmitterLifetimeEvents.java | 32 ++++++------- .../data/component/EmitterShape.java | 48 ++++++++++++------- .../component/ParticleInitialization.java | 15 +++--- .../component/ParticleLifeTimeEvents.java | 18 +++---- .../data/event/ParticleEffect.java | 8 +++- .../particle/MolangParticleLoader.java | 48 +++++++++++++------ .../particle/ParticleEmitter.java | 2 +- .../particle/ParticlePreset.java | 3 ++ 12 files changed, 125 insertions(+), 74 deletions(-) diff --git a/gradle.properties b/gradle.properties index f038542..e13b7df 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.2.2 +mod_version=1.2.3 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/org/mesdag/particlestorm/PSGameClient.java b/src/main/java/org/mesdag/particlestorm/PSGameClient.java index 5028d4d..3504d0f 100644 --- a/src/main/java/org/mesdag/particlestorm/PSGameClient.java +++ b/src/main/java/org/mesdag/particlestorm/PSGameClient.java @@ -121,9 +121,9 @@ public static void renderLevelStage(RenderLevelStageEvent event) { MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); for (ParticleEmitter emitter : LOADER.getEmitters()) { if (emitter.hideOutline) continue; - double x = Mth.lerp(partialTicks, emitter.posO.x, emitter.pos.x); - double y = Mth.lerp(partialTicks, emitter.posO.y, emitter.pos.y); - double z = Mth.lerp(partialTicks, emitter.posO.z, emitter.pos.z); + double x = Mth.lerp(partialTicks, emitter.posO.x, emitter.getPosition().x); + double y = Mth.lerp(partialTicks, emitter.posO.y, emitter.getPosition().y); + double z = Mth.lerp(partialTicks, emitter.posO.z, emitter.getPosition().z); DebugRenderer.renderFloatingText(poseStack, bufferSource, emitter.particleId.toString(), x, y + 0.5, z, 0xFFFFFF); DebugRenderer.renderFloatingText(poseStack, bufferSource, "id: " + emitter.id, x, y + 0.3, z, 0xFFFFFF); int maxNum = minecraft.particleEngine.trackedParticleCounts.getInt(emitter.particleGroup); @@ -175,7 +175,7 @@ private static void registerComponents() { IComponent.register("particle_initial_speed", ParticleInitialSpeed.CODEC); IComponent.register("particle_initial_spin", ParticleInitialSpin.CODEC); - IComponent.register("particle_initialization", ParticleInitialization.CODEC); + IComponent.register(ParticleInitialization.ID, ParticleInitialization.CODEC); IComponent.register(ParticleMotionDynamic.ID, ParticleMotionDynamic.CODEC); IComponent.register("particle_motion_parametric", ParticleMotionParametric.CODEC); diff --git a/src/main/java/org/mesdag/particlestorm/api/IEmitterComponent.java b/src/main/java/org/mesdag/particlestorm/api/IEmitterComponent.java index 13adc68..42197cf 100644 --- a/src/main/java/org/mesdag/particlestorm/api/IEmitterComponent.java +++ b/src/main/java/org/mesdag/particlestorm/api/IEmitterComponent.java @@ -3,9 +3,9 @@ import org.mesdag.particlestorm.particle.ParticleEmitter; public interface IEmitterComponent extends IComponent { - default void update(ParticleEmitter entity) {} + default void update(ParticleEmitter emitter) {} - default void apply(ParticleEmitter entity) {} + default void apply(ParticleEmitter emitter) {} default boolean requireUpdate() { return false; diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetime.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetime.java index 4195334..e55eac0 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetime.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetime.java @@ -2,12 +2,15 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.api.IEmitterComponent; +import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.data.molang.FloatMolangExp; import org.mesdag.particlestorm.data.molang.MolangExp; import org.mesdag.particlestorm.particle.ParticleEmitter; import java.util.List; +import java.util.Queue; public abstract sealed class EmitterLifetime implements IEmitterComponent permits EmitterLifetime.Expression, EmitterLifetime.Looping, EmitterLifetime.Once { /// Emitter will turn 'on' when the activation expression is non-zero, and will turn 'off' when it's zero. @@ -127,6 +130,14 @@ public void update(ParticleEmitter emitter) { e.apply(emitter); } emitter.updateRandoms(emitter.level.random); + Queue queue = PSGameClient.LOADER.getParticlesForEmitter(emitter); + if (queue != null) { + for (IMolangParticleInstance instance : queue) { + FloatMolangExp exp = instance.getPreset().perUpdateExpression; + if (exp == null) continue; + exp.calculate(instance); + } + } } } diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetimeEvents.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetimeEvents.java index 0ea5ec1..b85b43d 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetimeEvents.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetimeEvents.java @@ -2,7 +2,8 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.util.Tuple; +import it.unimi.dsi.fastutil.floats.FloatObjectImmutablePair; +import it.unimi.dsi.fastutil.floats.FloatObjectPair; import org.mesdag.particlestorm.ParticleStorm; import org.mesdag.particlestorm.api.IEmitterComponent; import org.mesdag.particlestorm.api.IEventNode; @@ -13,7 +14,6 @@ import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.function.Function; /// Allows for lifetime events on the emitter to trigger certain events. /// @@ -34,8 +34,8 @@ public final class EmitterLifetimeEvents implements IEmitterComponent { public final Map> travelDistanceEvents; public final List loopingTravelDistanceEvents; - public final List, List>> sortedTimeline; - public final List, List>> sortedTravelDistance; + public final List>> sortedTimeline; + public final List>> sortedTravelDistance; /// @param creationEvent Fires when the emitter is created /// @param expirationEvent Fires when the emitter expires (does not wait for particles to expire too) @@ -54,14 +54,14 @@ public EmitterLifetimeEvents(List creationEvent, List expiration this.sortedTimeline = new ArrayList<>(); timeline.entrySet().stream() - .map(entry -> new Tuple<>(Float.parseFloat(entry.getKey()), entry.getValue())) - .sorted(Comparator.comparing(Tuple::getA)) - .forEachOrdered(tuple -> sortedTimeline.add(new Tuple<>(time -> time >= tuple.getA() * 20, tuple.getB()))); + .map(entry -> new FloatObjectImmutablePair<>(Float.parseFloat(entry.getKey()) * 20, entry.getValue())) + .sorted(Comparator.comparing(FloatObjectPair::leftFloat)) + .forEachOrdered(sortedTimeline::add); this.sortedTravelDistance = new ArrayList<>(); travelDistanceEvents.entrySet().stream() - .map(entry -> new Tuple<>(Float.parseFloat(entry.getKey()), entry.getValue())) - .sorted(Comparator.comparing(Tuple::getA)) - .forEachOrdered(tuple -> sortedTravelDistance.add(new Tuple<>(dist -> dist >= tuple.getA(), tuple.getB()))); + .map(entry -> new FloatObjectImmutablePair<>(Float.parseFloat(entry.getKey()), entry.getValue())) + .sorted(Comparator.comparing(FloatObjectPair::leftFloat)) + .forEachOrdered(sortedTravelDistance::add); } @Override @@ -77,19 +77,19 @@ public List getAllMolangExp() { @Override public void update(ParticleEmitter emitter) { for (int i = emitter.lastTimeline; i < sortedTimeline.size(); i++) { - Tuple, List> tuple = sortedTimeline.get(i); - if (tuple.getA().apply(emitter.lifetime)) { + FloatObjectPair> pair = sortedTimeline.get(i); + if (emitter.age >= pair.leftFloat()) { emitter.lastTimeline = i + 1; - executes(emitter, tuple.getB()); + executes(emitter, pair.right()); break; } } if (emitter.moveDist == emitter.moveDistO) return; for (int i = emitter.lastTravelDist; i < sortedTravelDistance.size(); i++) { - Tuple, List> tuple = sortedTravelDistance.get(i); - if (tuple.getA().apply(emitter.moveDist)) { + FloatObjectPair> pair = sortedTravelDistance.get(i); + if (emitter.moveDist >= pair.leftFloat()) { emitter.lastTravelDist = i + 1; - executes(emitter, tuple.getB()); + executes(emitter, pair.right()); break; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java index f86d0a6..64dec16 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java @@ -9,10 +9,12 @@ import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.StringRepresentable; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.phys.Vec3; import org.joml.Quaternionf; import org.joml.Vector3f; +import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.api.*; import org.mesdag.particlestorm.data.MathHelper; import org.mesdag.particlestorm.data.molang.FloatMolangExp; @@ -38,16 +40,16 @@ public boolean isSurfaceOnly() { } @Override - public void update(ParticleEmitter entity) { - if (entity.spawned) return; - if (entity.spawnDuration <= 1 || entity.age % entity.spawnDuration == 0) { - for (int num = 0; num < entity.spawnRate; num++) { - if (hasSpaceInParticleLimit(entity)) { - emittingParticle(entity); + public void update(ParticleEmitter emitter) { + if (emitter.spawned) return; + if (emitter.spawnDuration <= 1 || emitter.age % emitter.spawnDuration == 0) { + for (int num = 0; num < emitter.spawnRate; num++) { + if (hasSpaceInParticleLimit(emitter)) { + emittingParticle(emitter); } } - if (entity.getPreset().emitterRateType == EmitterRate.Type.INSTANT) { - entity.spawned = true; + if (emitter.getPreset().emitterRateType == EmitterRate.Type.INSTANT) { + emitter.spawned = true; } } } @@ -88,17 +90,28 @@ private void emittingParticle(Par speed.x *= -1; speed.z *= -1; } - if (emitterPreset.localRotation) { - MathHelper.applyEuler(emitter.rot.x, emitter.rot.y, 0.0F, position); - } - if (emitterPreset.localPosition) { + speed.mul(emitter.invTickRate); + Entity attachedEntity = emitter.getAttachedEntity(); + if (attachedEntity == null) { Vec3 emitterPos = emitter.getPosition(); position.add((float) emitterPos.x, (float) emitterPos.y, (float) emitterPos.z); - } - speed.mul(emitter.invTickRate); - if (emitter.getAttachedEntity() != null && emitterPreset.localVelocity) { - Vec3 emitterVec = emitter.getAttachedEntity().getDeltaMovement(); - speed.add((float) emitterVec.x, (float) emitterVec.y, (float) emitterVec.z); + } else { + // region todo 改为渲染时 +// if (emitter.parentMode == ParticleEmitter.ParentMode.LOCATOR) { +// +// } + if (emitterPreset.localRotation) { + MathHelper.applyEuler(emitter.rot.x, emitter.rot.y, 0.0F, position); + } + if (!emitterPreset.localPosition) { + Vec3 emitterPos = emitter.getPosition(); + position.add((float) emitterPos.x, (float) emitterPos.y, (float) emitterPos.z); + } + // endregion + if (emitterPreset.localVelocity) { + Vec3 emitterVec = attachedEntity.getDeltaMovement(); + speed.add((float) emitterVec.x, (float) emitterVec.y, (float) emitterVec.z); + } } instance.setParticleSpeed(speed.x, speed.y, speed.z); @@ -115,6 +128,7 @@ private void emittingParticle(Par instance.setComponents(particlePreset.effect.orderedParticleComponentsWhichRequireUpdate); if (!particlePreset.motionDynamic) instance.setParticleSpeed(0.0, 0.0, 0.0); Minecraft.getInstance().particleEngine.add(instance); + PSGameClient.LOADER.addParticleForEmitter(instance); } private static boolean hasSpaceInParticleLimit(ParticleEmitter emitter) { diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleInitialization.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleInitialization.java index cf4e171..c087e3c 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleInitialization.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleInitialization.java @@ -2,6 +2,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.resources.ResourceLocation; import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.IParticleComponent; import org.mesdag.particlestorm.data.molang.FloatMolangExp; @@ -10,16 +11,12 @@ import java.util.List; /// Starts the particle with a specified render expression. -public record ParticleInitialization(FloatMolangExp perRenderExpression) implements IParticleComponent { +public record ParticleInitialization(FloatMolangExp perRenderExpression, FloatMolangExp perUpdateExpression) implements IParticleComponent { + public static final ResourceLocation ID = ResourceLocation.withDefaultNamespace("particle_initialization"); public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - FloatMolangExp.CODEC.fieldOf("per_render_expression").forGetter(ParticleInitialization::perRenderExpression), + FloatMolangExp.CODEC.lenientOptionalFieldOf("per_render_expression", FloatMolangExp.ZERO).forGetter(ParticleInitialization::perRenderExpression), FloatMolangExp.CODEC.lenientOptionalFieldOf("per_update_expression", FloatMolangExp.ZERO).forGetter(ParticleInitialization::perRenderExpression) - ).apply(instance, (render, update) -> { - if (update != FloatMolangExp.ZERO) { - throw new IllegalArgumentException("per_update_expression is not allowed here, please use per_render_expression instead"); - } - return new ParticleInitialization(render); - })); + ).apply(instance, ParticleInitialization::new)); @Override public Codec codec() { @@ -28,7 +25,7 @@ public Codec codec() { @Override public List getAllMolangExp() { - return List.of(perRenderExpression); + return List.of(perRenderExpression, perUpdateExpression); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleLifeTimeEvents.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleLifeTimeEvents.java index 00773c8..3bbef1d 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleLifeTimeEvents.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleLifeTimeEvents.java @@ -2,8 +2,9 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import it.unimi.dsi.fastutil.floats.FloatObjectImmutablePair; +import it.unimi.dsi.fastutil.floats.FloatObjectPair; import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.Tuple; import org.mesdag.particlestorm.ParticleStorm; import org.mesdag.particlestorm.api.IEventNode; import org.mesdag.particlestorm.api.IMolangParticleInstance; @@ -14,7 +15,6 @@ import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.function.Function; /// All events use the event names in the event section /// @@ -30,7 +30,7 @@ public final class ParticleLifeTimeEvents implements IParticleComponent { public final List expirationEvent; public final Map> timeline; - public final List, List>> sortedTimeline; + public final List>> sortedTimeline; /// @param creationEvent Fires when the particle is created /// @param expirationEvent Fires when the particle expires (does not wait for particles to expire too) @@ -42,9 +42,9 @@ public ParticleLifeTimeEvents(List creationEvent, List expiratio this.sortedTimeline = new ArrayList<>(); timeline.entrySet().stream() - .map(entry -> new Tuple<>(Float.parseFloat(entry.getKey()), entry.getValue())) - .sorted(Comparator.comparing(Tuple::getA)) - .forEachOrdered(tuple -> sortedTimeline.add(new Tuple<>(time -> time >= tuple.getA() * 20, tuple.getB()))); + .map(entry -> new FloatObjectImmutablePair<>(Float.parseFloat(entry.getKey()) * 20, entry.getValue())) + .sorted(Comparator.comparing(FloatObjectPair::leftFloat)) + .forEachOrdered(sortedTimeline::add); } @Override @@ -60,10 +60,10 @@ public List getAllMolangExp() { @Override public void update(IMolangParticleInstance instance) { for (int i = instance.getLastTimeline(); i < sortedTimeline.size(); i++) { - Tuple, List> tuple = sortedTimeline.get(i); - if (tuple.getA().apply(instance.self().getLifetime())) { + FloatObjectPair> pair = sortedTimeline.get(i); + if (instance.getAge() >= pair.leftFloat()) { instance.setLastTimeline(i + 1); - executes(instance, tuple.getB()); + executes(instance, pair.right()); break; } } diff --git a/src/main/java/org/mesdag/particlestorm/data/event/ParticleEffect.java b/src/main/java/org/mesdag/particlestorm/data/event/ParticleEffect.java index 3c12962..8d50d1b 100644 --- a/src/main/java/org/mesdag/particlestorm/data/event/ParticleEffect.java +++ b/src/main/java/org/mesdag/particlestorm/data/event/ParticleEffect.java @@ -11,6 +11,7 @@ import net.minecraft.util.StringRepresentable; import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.api.IEventNode; +import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.MolangExp; import org.mesdag.particlestorm.data.molang.compiler.MolangQueries; @@ -37,7 +38,12 @@ public ParticleEffect(ResourceLocation effect, Type type, MolangExp preEffectExp @Override public void execute(MolangInstance instance) { - PSGameClient.LOADER.addEmitter(new ParticleEmitter(instance.getEmitter(), this), false); + ParticleEmitter emitter = new ParticleEmitter(instance.getEmitter(), this); + if (instance instanceof IMolangParticleInstance) { + emitter.setPos(instance.getPosition()); + emitter.posO = emitter.getPosition(); + } + PSGameClient.LOADER.addEmitter(emitter, false); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java index 423b4ad..351ea3f 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java @@ -33,20 +33,18 @@ import java.io.IOException; import java.io.Reader; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -@SuppressWarnings("all") public class MolangParticleLoader implements PreparableReloadListener { private static final FileToIdConverter PARTICLE_LISTER = FileToIdConverter.json("particle_definitions"); private Map id2Effect = new Hashtable<>(); private Map id2Particle = new Hashtable<>(); private Map id2Emitter = new Hashtable<>(); private final Int2ObjectOpenHashMap emitters = new Int2ObjectOpenHashMap<>(); - private final Object2ObjectMap> tracker = new Object2ObjectOpenHashMap<>(); + private final Object2ObjectOpenHashMap> tracker = new Object2ObjectOpenHashMap<>(); + private final Int2ObjectOpenHashMap> particlesForEmitter = new Int2ObjectOpenHashMap<>(); private final IntAllocator allocator = new IntAllocator(); private boolean initialized = false; @@ -63,12 +61,13 @@ public Map id2Emitter() { return id2Emitter; } - public void tick(LocalPlayer localPlayer) { + @SuppressWarnings("CallToPrintStackTrace") + public void tick(LocalPlayer player) { if (!initialized) { for (ParticlePreset detail : id2Particle.values()) { for (IComponent component : detail.effect.orderedComponents) { if (component instanceof IParticleComponent particleComponent) { - particleComponent.initialize(localPlayer.level()); + particleComponent.initialize(player.level()); } } } @@ -81,12 +80,16 @@ public void tick(LocalPlayer localPlayer) { while (iterator.hasNext()) { ParticleEmitter emitter = iterator.next().getValue(); try { - if (emitter.isRemoved() || emitter.level.dimension() != localPlayer.level().dimension()) { + if (emitter.isRemoved() || emitter.level.dimension() != player.level().dimension()) { emitter.onRemove(); emitter.remove(); iterator.remove(); allocator.release(emitter.id); - } else if (Mth.lengthSquared(emitter.pos.x - localPlayer.getX(), emitter.pos.z - localPlayer.getZ()) < renderDistSqr) { + particlesForEmitter.remove(emitter.id); + } else if (Mth.lengthSquared( + emitter.getPosition().x - player.getX(), + emitter.getPosition().z - player.getZ() + ) < renderDistSqr) { emitter.tick(); } } catch (Exception e) { @@ -94,22 +97,30 @@ public void tick(LocalPlayer localPlayer) { e.printStackTrace(); if (emitter != null) { emitter.remove(); + allocator.release(emitter.id); + particlesForEmitter.remove(emitter.id); } iterator.remove(); } } } if (!tracker.isEmpty()) { - ObjectIterator>> iterator1 = tracker.entrySet().iterator(); - while (iterator1.hasNext()) { - Map.Entry> entry = iterator1.next(); + ObjectIterator>> iterator = tracker.object2ObjectEntrySet().fastIterator(); + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); if (entry.getKey().isRemoved()) { - iterator1.remove(); + iterator.remove(); } else if (entry.getValue().removeIf(ParticleEmitter::isRemoved) && entry.getValue().isEmpty()) { - iterator1.remove(); + iterator.remove(); } } } + if (!particlesForEmitter.isEmpty()) { + ObjectIterator>> iterator = particlesForEmitter.int2ObjectEntrySet().fastIterator(); + while (iterator.hasNext()) { + iterator.next().getValue().removeIf(IMolangParticleInstance::isDiscarded); + } + } } public Iterable getEmitters() { @@ -149,6 +160,14 @@ public boolean addTrackedEmitter(Entity entity, ResourceLocation particleId) { return true; } + public void addParticleForEmitter(IMolangParticleInstance instance) { + particlesForEmitter.computeIfAbsent(instance.getEmitter().id, i -> new ArrayDeque<>()).add(instance); + } + + public @Nullable Queue getParticlesForEmitter(ParticleEmitter emitter) { + return particlesForEmitter.get(emitter.id); + } + public void removeEmitter(ParticleEmitter emitter, boolean sync) { removeEmitter(emitter.id, sync); } @@ -171,6 +190,7 @@ public void removeAll() { iterator.remove(); } } + particlesForEmitter.clear(); tracker.clear(); allocator.clear(); } diff --git a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java index 108784d..a88c806 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java +++ b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java @@ -75,7 +75,7 @@ public class ParticleEmitter implements MolangInstance { public transient float[] cachedLooping; public transient final Level level; - public Vec3 pos; + protected Vec3 pos; public Vec3 posO = Vec3.ZERO; public Vector3f rot = new Vector3f(); public boolean hideOutline; diff --git a/src/main/java/org/mesdag/particlestorm/particle/ParticlePreset.java b/src/main/java/org/mesdag/particlestorm/particle/ParticlePreset.java index 9556f9c..4487943 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/ParticlePreset.java +++ b/src/main/java/org/mesdag/particlestorm/particle/ParticlePreset.java @@ -37,6 +37,7 @@ public class ParticlePreset { public float invTextureWidth; public float invTextureHeight; public boolean motionDynamic; + public @Nullable FloatMolangExp perUpdateExpression; public VariableTable vars; public List assignments; @@ -80,6 +81,8 @@ public ParticlePreset(DefinedParticleEffect effect) { ParticleMotionCollision motionCollision = (ParticleMotionCollision) effect.components.get(ParticleMotionCollision.ID); if (motionCollision != null) this.collisionEvents = motionCollision.events(); this.motionDynamic = effect.components.get(ParticleMotionDynamic.ID) != null; + ParticleInitialization initialization = (ParticleInitialization) effect.components.get(ParticleInitialization.ID); + if (initialization != null) this.perUpdateExpression = initialization.perUpdateExpression(); VariableTable table = new VariableTable(addDefaultVariables(), null); MolangParser parser = new MolangParser(table); From f314bed51993fd23c5f4f53c3b2c1ba9d626b56b Mon Sep 17 00:00:00 2001 From: westernat Date: Tue, 26 May 2026 20:06:42 +0800 Subject: [PATCH 11/13] =?UTF-8?q?=E4=BF=AE=E6=AD=A3localPosition=E3=80=81l?= =?UTF-8?q?ocalRotation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + gradle.properties | 2 +- .../mesdag/particlestorm/PSGameClient.java | 71 +++++--- .../mesdag/particlestorm/ParticleStorm.java | 15 +- .../api/IMolangParticleInstance.java | 6 + .../api/RegisterCustomParticleTypeEvent.java | 4 +- .../api/geckolib/GeckoLibHelper.java | 106 +++++++---- ...va => ParticleStormGeoReplacedEntity.java} | 2 +- .../RegisterLocatorPreTransformerEvent.java | 147 ++++++++++++++++ .../api/geckolib/ReplacedCreeperEntity.java | 9 +- .../api/geckolib/ReplacedCreeperModel.java | 10 -- .../api/geckolib/ReplacedCreeperRenderer.java | 6 +- .../data/component/EmitterLifetime.java | 4 +- .../data/component/EmitterShape.java | 45 ++--- .../component/ParticleExpireIfInBlocks.java | 16 +- .../ParticleExpireIfNotInBlocks.java | 16 +- .../component/ParticleMotionCollision.java | 1 + .../data/event/NodeMolangExp.java | 13 +- .../data/event/ParticleEffect.java | 13 +- .../particlestorm/data/event/SoundEffect.java | 13 +- .../data/molang/compiler/MolangQueries.java | 7 +- .../mixed/IAnimatableInstanceCache.java | 6 +- .../mesdag/particlestorm/mixed/IGeoBone.java | 3 +- .../mixed/IParticleKeyframeData.java | 5 + .../mixin/LivingEntityMixin.java | 4 +- .../mixin/ParticleEngineMixin.java | 30 ++-- .../AnimatableInstanceCacheMixin.java | 9 +- .../geckolib/AnimationControllerMixin.java | 2 +- .../geckolib/AnimationProcessorMixin.java | 5 +- .../BakedModelFactory$BuiltinMixin.java | 4 +- .../integration/geckolib/GeoBoneMixin.java | 37 +--- .../integration/geckolib/GeoModelMixin.java | 28 +++ .../geckolib/GeoReplacedEntityMixin.java | 10 ++ .../GeoReplacedEntityRendererMixin.java | 5 +- .../geckolib/MolangQueriesMixin.java | 13 +- .../network/EmitterAttachPacketS2C.java | 4 +- .../network/EmitterCreationPacketS2C.java | 4 +- .../network/EmitterRemovalPacket.java | 4 +- .../network/EmitterSynchronizePacket.java | 4 +- ...eLoader.java => MolangParticleEngine.java} | 164 ++++++++++++++---- .../particle/MolangParticleInstance.java | 47 ++++- .../particle/ParticleEmitter.java | 70 ++++---- .../resources/META-INF/accesstransformer.cfg | 2 +- .../block/test_block.animation.json | 9 +- .../geo/block/test_block.geo.json | 25 ++- .../particle_definitions/blend.json | 4 - .../loading.particle.json | 7 +- src/main/resources/particlestorm.mixins.json | 5 +- 48 files changed, 703 insertions(+), 315 deletions(-) rename src/main/java/org/mesdag/particlestorm/api/geckolib/{GeoWithCurrentEntity.java => ParticleStormGeoReplacedEntity.java} (79%) create mode 100644 src/main/java/org/mesdag/particlestorm/api/geckolib/RegisterLocatorPreTransformerEvent.java delete mode 100644 src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperModel.java create mode 100644 src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoModelMixin.java create mode 100644 src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityMixin.java rename src/main/java/org/mesdag/particlestorm/particle/{MolangParticleLoader.java => MolangParticleEngine.java} (57%) diff --git a/.gitignore b/.gitignore index ddb049c..ce2a361 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ build eclipse run run-data + +app.js diff --git a/gradle.properties b/gradle.properties index e13b7df..fb65e94 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ mod_name=ParticleStorm # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0 # The mod version. See https://semver.org/ -mod_version=1.2.3 +mod_version=1.3.0 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/org/mesdag/particlestorm/PSGameClient.java b/src/main/java/org/mesdag/particlestorm/PSGameClient.java index 3504d0f..f3c045e 100644 --- a/src/main/java/org/mesdag/particlestorm/PSGameClient.java +++ b/src/main/java/org/mesdag/particlestorm/PSGameClient.java @@ -20,20 +20,25 @@ import net.neoforged.fml.ModLoader; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.client.event.*; +import org.jetbrains.annotations.ApiStatus; import org.mesdag.particlestorm.api.*; import org.mesdag.particlestorm.api.geckolib.GeckoLibHelper; import org.mesdag.particlestorm.data.component.*; import org.mesdag.particlestorm.data.event.*; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.MolangParticleInstance; -import org.mesdag.particlestorm.particle.MolangParticleLoader; import org.mesdag.particlestorm.particle.ParticleEmitter; import java.io.IOException; +import java.util.Queue; @EventBusSubscriber(modid = ParticleStorm.MODID, value = Dist.CLIENT) public final class PSGameClient { - public static final MolangParticleLoader LOADER = new MolangParticleLoader(); + @Deprecated(forRemoval = true, since = "1.3.0") + @ApiStatus.ScheduledForRemoval(inVersion = "1.4.0") + public static final MolangParticleEngine LOADER = MolangParticleEngine.INSTANCE; public static final ParticleRenderType PARTICLE_ADD = new ParticleRenderType() { @Override public BufferBuilder begin(Tesselator tesselator, TextureManager textureManager) { @@ -99,7 +104,7 @@ public static void registerRenderers(EntityRenderersEvent.RegisterRenderers even @SubscribeEvent public static void clientNetwork$LoggingOut(ClientPlayerNetworkEvent.LoggingOut event) { - LOADER.removeAll(); + MolangParticleEngine.INSTANCE.removeAll(); } @SubscribeEvent @@ -107,36 +112,39 @@ public static void tick(ClientTickEvent.Pre event) { Minecraft minecraft = Minecraft.getInstance(); LocalPlayer player = minecraft.player; if (player != null && !minecraft.isPaused() && player.clientLevel.tickRateManager().runsNormally()) { - LOADER.tick(player); + MolangParticleEngine.INSTANCE.tick(minecraft, player); } } @SubscribeEvent public static void renderLevelStage(RenderLevelStageEvent event) { + if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_PARTICLES) return; if (!PSClientConfigs.showEmitterOutline) return; Minecraft minecraft = Minecraft.getInstance(); - if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_PARTICLES && minecraft.getEntityRenderDispatcher().shouldRenderHitBoxes()) { - float partialTicks = event.getPartialTick().getGameTimeDeltaPartialTick(true); - PoseStack poseStack = event.getPoseStack(); - MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); - for (ParticleEmitter emitter : LOADER.getEmitters()) { - if (emitter.hideOutline) continue; - double x = Mth.lerp(partialTicks, emitter.posO.x, emitter.getPosition().x); - double y = Mth.lerp(partialTicks, emitter.posO.y, emitter.getPosition().y); - double z = Mth.lerp(partialTicks, emitter.posO.z, emitter.getPosition().z); - DebugRenderer.renderFloatingText(poseStack, bufferSource, emitter.particleId.toString(), x, y + 0.5, z, 0xFFFFFF); - DebugRenderer.renderFloatingText(poseStack, bufferSource, "id: " + emitter.id, x, y + 0.3, z, 0xFFFFFF); - int maxNum = minecraft.particleEngine.trackedParticleCounts.getInt(emitter.particleGroup); - DebugRenderer.renderFloatingText(poseStack, bufferSource, "particles: " + maxNum, x, y + 0.1, z, maxNum >= emitter.particleGroup.getLimit() ? 0xFF0000 : 0xFFFFFF); - Camera camera = event.getCamera(); - double d0 = camera.getPosition().x; - double d1 = camera.getPosition().y; - double d2 = camera.getPosition().z; - poseStack.pushPose(); - poseStack.translate(x - d0, y - d1, z - d2); - LevelRenderer.renderLineBox(poseStack, bufferSource.getBuffer(RenderType.lines()), -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0, 1, 0, 1); - poseStack.popPose(); - } + if (!minecraft.getEntityRenderDispatcher().shouldRenderHitBoxes()) return; + PoseStack poseStack = event.getPoseStack(); + MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); + Camera camera = event.getCamera(); + double camX = camera.getPosition().x; + double camY = camera.getPosition().y; + double camZ = camera.getPosition().z; + float partialTick = event.getPartialTick().getGameTimeDeltaPartialTick(true); + var iterator = MolangParticleEngine.INSTANCE.getEmitters(); + while (iterator.hasNext()) { + ParticleEmitter emitter = iterator.next().getValue(); + if (emitter.hideOutline) continue; + double x = Mth.lerp(partialTick, emitter.posO.x, emitter.getX()); + double y = Mth.lerp(partialTick, emitter.posO.y, emitter.getY()); + double z = Mth.lerp(partialTick, emitter.posO.z, emitter.getZ()); + DebugRenderer.renderFloatingText(poseStack, bufferSource, emitter.particleId.toString(), x, y + 0.5, z, 0xFFFFFF); + DebugRenderer.renderFloatingText(poseStack, bufferSource, "id: " + emitter.id, x, y + 0.3, z, 0xFFFFFF); + Queue queue = MolangParticleEngine.INSTANCE.getParticlesForEmitter(emitter); + int count = queue == null ? 0 : queue.size(); + DebugRenderer.renderFloatingText(poseStack, bufferSource, "particles: " + count, x, y + 0.1, z, count >= emitter.particleGroup.getLimit() ? 0xFF0000 : 0xFFFFFF); + poseStack.pushPose(); + poseStack.translate(x - camX, y - camY, z - camZ); + LevelRenderer.renderLineBox(poseStack, bufferSource.getBuffer(RenderType.lines()), -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0, 1, 0, 1); + poseStack.popPose(); } } @@ -144,7 +152,7 @@ public static void renderLevelStage(RenderLevelStageEvent event) { public static void reload(RegisterClientReloadListenersEvent event) { registerComponents(); registerEventNodes(); - event.registerReloadListener(LOADER); + event.registerReloadListener(MolangParticleEngine.INSTANCE); } @SubscribeEvent @@ -205,4 +213,13 @@ private static void registerEventNodes() { ModLoader.postEvent(new RegisterCustomEventNodeEvent()); } + + @SubscribeEvent + public static void fmlClientSetup(FMLClientSetupEvent event) { + event.enqueueWork(() -> { + if (ParticleStorm.GECKOLIB_LOADED) { + GeckoLibHelper.postEvent(); + } + }); + } } diff --git a/src/main/java/org/mesdag/particlestorm/ParticleStorm.java b/src/main/java/org/mesdag/particlestorm/ParticleStorm.java index fe55c0a..026735a 100644 --- a/src/main/java/org/mesdag/particlestorm/ParticleStorm.java +++ b/src/main/java/org/mesdag/particlestorm/ParticleStorm.java @@ -19,7 +19,6 @@ import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; -import net.neoforged.neoforge.network.registration.PayloadRegistrar; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; import org.mesdag.particlestorm.api.geckolib.GeckoLibHelper; @@ -42,9 +41,10 @@ public final class ParticleStorm { public static final String MODID = "particlestorm"; public static final Logger LOGGER = LoggerFactory.getLogger("ParticleStorm"); - public static final boolean DEBUG = Boolean.getBoolean("particlestorm.debug") && LoadingModList.get().getModFileById("geckolib") != null; + public static final boolean GECKOLIB_LOADED = LoadingModList.get().getModFileById("geckolib") != null; public static final boolean SODIUM_LOADED = LoadingModList.get().getModFileById("sodium") != null; public static final boolean IRIS_LOADED = LoadingModList.get().getModFileById("iris") != null; + public static final boolean DEBUG = Boolean.getBoolean("particlestorm.debug") && GECKOLIB_LOADED; private static final DeferredRegister> REGISTER = DeferredRegister.create(BuiltInRegistries.PARTICLE_TYPE, MODID); public static final DeferredHolder, ParticleType> MOLANG = registerParticleType(REGISTER, "molang"); @@ -63,11 +63,12 @@ public ParticleStorm(IEventBus bus, ModContainer container) { } private static void registerPayloadHandlers(RegisterPayloadHandlersEvent event) { - PayloadRegistrar registrar = event.registrar("1"); - registrar.playToClient(EmitterCreationPacketS2C.TYPE, EmitterCreationPacketS2C.STREAM_CODEC, EmitterCreationPacketS2C::handle); - registrar.playToClient(EmitterAttachPacketS2C.TYPE, EmitterAttachPacketS2C.STREAM_CODEC, EmitterAttachPacketS2C::handle); - registrar.playBidirectional(EmitterRemovalPacket.TYPE, EmitterRemovalPacket.STREAM_CODEC, EmitterRemovalPacket::handle); - registrar.playBidirectional(EmitterSynchronizePacket.TYPE, EmitterSynchronizePacket.STREAM_CODEC, EmitterSynchronizePacket::handle); + event.registrar("1") + .playToClient(EmitterCreationPacketS2C.TYPE, EmitterCreationPacketS2C.STREAM_CODEC, EmitterCreationPacketS2C::handle) + .playToClient(EmitterAttachPacketS2C.TYPE, EmitterAttachPacketS2C.STREAM_CODEC, EmitterAttachPacketS2C::handle) + .playBidirectional(EmitterRemovalPacket.TYPE, EmitterRemovalPacket.STREAM_CODEC, EmitterRemovalPacket::handle) + .playBidirectional(EmitterSynchronizePacket.TYPE, EmitterSynchronizePacket.STREAM_CODEC, EmitterSynchronizePacket::handle) + ; } private static void registerCommands(RegisterCommandsEvent event) { diff --git a/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java b/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java index 7d12ce9..cc38b37 100644 --- a/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java +++ b/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java @@ -1,6 +1,8 @@ package org.mesdag.particlestorm.api; +import net.minecraft.client.Camera; import net.minecraft.client.particle.Particle; +import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.particles.ParticleGroup; import net.minecraft.resources.ResourceLocation; @@ -141,5 +143,9 @@ default Vec3 getPosition() { default float getInvTickRate() { return getEmitter().invTickRate; } + + default boolean isVisible(Camera camera, Frustum frustum, float partialTick) { + return frustum.isVisible(self().getRenderBoundingBox(partialTick)); + } // endregion } diff --git a/src/main/java/org/mesdag/particlestorm/api/RegisterCustomParticleTypeEvent.java b/src/main/java/org/mesdag/particlestorm/api/RegisterCustomParticleTypeEvent.java index 3b6f652..73832bf 100644 --- a/src/main/java/org/mesdag/particlestorm/api/RegisterCustomParticleTypeEvent.java +++ b/src/main/java/org/mesdag/particlestorm/api/RegisterCustomParticleTypeEvent.java @@ -11,8 +11,8 @@ import net.neoforged.fml.ModLoader; import net.neoforged.fml.event.IModBusEvent; import org.jetbrains.annotations.Contract; -import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.particle.ExtendMutableSpriteSet; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.ParticleEmitter; import org.mesdag.particlestorm.particle.ParticlePreset; @@ -55,7 +55,7 @@ public static V createParticle(Pa throw new NullPointerException("Provider from '" + BuiltInRegistries.PARTICLE_TYPE.getKey(emitter.getPreset().type) + "' is not registered"); } - return (V) provider.create(emitter, PSGameClient.LOADER.id2Particle().get(emitter.particleId), (ClientLevel) emitter.level, emitter.getX(), emitter.getY(), emitter.getZ(), sprites); + return (V) provider.create(emitter, MolangParticleEngine.INSTANCE.id2Particle().get(emitter.particleId), (ClientLevel) emitter.level, emitter.getX(), emitter.getY(), emitter.getZ(), sprites); } @FunctionalInterface diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java index 3c6c6e0..0000fd2 100644 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/GeckoLibHelper.java @@ -1,5 +1,6 @@ package org.mesdag.particlestorm.api.geckolib; +import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.datafixers.DSL; import it.unimi.dsi.fastutil.ints.IntIterator; import net.minecraft.core.registries.BuiltInRegistries; @@ -12,16 +13,17 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.phys.Vec3; import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModLoader; import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; import org.jetbrains.annotations.Nullable; -import org.joml.Vector3f; -import org.mesdag.particlestorm.PSGameClient; +import org.joml.Quaternionf; import org.mesdag.particlestorm.ParticleStorm; import org.mesdag.particlestorm.data.molang.MolangExp; import org.mesdag.particlestorm.data.molang.VariableTable; import org.mesdag.particlestorm.mixed.*; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.ParticleEmitter; import software.bernie.geckolib.animatable.GeoAnimatable; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; @@ -31,9 +33,10 @@ import software.bernie.geckolib.loading.json.raw.LocatorValue; import java.util.List; +import java.util.Map; public final class GeckoLibHelper { - public static DeferredHolder, BlockEntityType> TEST_ENTITY; + static DeferredHolder, BlockEntityType> TEST_ENTITY; public static void registerStuffs(IEventBus bus) { DeferredRegister BLOCK = DeferredRegister.create(BuiltInRegistries.BLOCK, ParticleStorm.MODID); @@ -45,52 +48,53 @@ public static void registerStuffs(IEventBus bus) { } public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) { - event.registerBlockEntityRenderer(GeckoLibHelper.TEST_ENTITY.get(), ExampleBlockEntityRenderer::new); + event.registerBlockEntityRenderer(TEST_ENTITY.get(), ExampleBlockEntityRenderer::new); event.registerEntityRenderer(EntityType.CREEPER, ReplacedCreeperRenderer::new); } - public static double[] getLocatorOffset(Object locatorValue) { - LocatorValue value = (LocatorValue) locatorValue; - if (value.locatorClass() == null) { - return value.values(); + public static void postEvent() { + ModLoader.postEvent(new RegisterLocatorPreTransformerEvent()); + } + + public static double[] getLocatorOffset(LocatorValue locatorValue) { + if (locatorValue.locatorClass() == null) { + return locatorValue.values(); } - return value.locatorClass().offset(); + return locatorValue.locatorClass().offset(); } - public static double[] getLocatorRotation(Object locatorValue) { - LocatorValue value = (LocatorValue) locatorValue; - if (value.locatorClass() == null) { + public static double[] getLocatorRotation(LocatorValue locatorValue) { + if (locatorValue.locatorClass() == null) { return new double[3]; } - return value.locatorClass().rotation(); + return locatorValue.locatorClass().rotation(); } - /** - * @return true means failed to add emitter - */ - public static boolean processParticleEffect(Object a, Object c, Object d) { - List bones = IAnimationController.of((AnimationController) c).particlestorm$getBonesWhichHasLocators(); + /// @return true means failed to add emitter + public static boolean processParticleEffect(@Nullable GeoAnimatable animatable, AnimationController controller, ParticleKeyframeData keyframeData) { + List bones = IAnimationController.of(controller).particlestorm$getBonesWhichHasLocators(); if (bones.isEmpty()) return true; - ParticleKeyframeData keyframeData = (ParticleKeyframeData) d; - IParticleKeyframeData iData = (IParticleKeyframeData) keyframeData; - Entity entity = null; - BlockEntity blockEntity = null; + IParticleKeyframeData iData = IParticleKeyframeData.of(keyframeData); + Entity entity; + BlockEntity blockEntity; VariableTable variableTable; Level level; - GeoAnimatable animatable = (GeoAnimatable) a; switch (animatable) { case Entity entity1 -> { entity = entity1; + blockEntity = null; variableTable = IEntity.of(entity).particlestorm$getVariableTable(); level = entity.level(); } - case GeoWithCurrentEntity withCurrentEntity when withCurrentEntity.getCurrentEntity() != null -> { + case ParticleStormGeoReplacedEntity withCurrentEntity when withCurrentEntity.getCurrentEntity() != null -> { entity = withCurrentEntity.getCurrentEntity(); + blockEntity = null; variableTable = IEntity.of(entity).particlestorm$getVariableTable(); level = entity.level(); } case BlockEntity entity1 when entity1.getLevel() != null -> { + entity = null; blockEntity = entity1; variableTable = ((IBlockEntity) blockEntity).particlestorm$getVariableTable(); level = blockEntity.getLevel(); @@ -106,40 +110,68 @@ public static boolean processParticleEffect(Object a, Object c, Object d) { LocatorValue locator = IGeoBone.of(bone).particlestorm$getLocators().get(keyframeData.getLocator()); if (locator == null) continue; - ParticleEmitter current = PSGameClient.LOADER.getEmitter(cache.particlestorm$getCachedId().getInt(locator)); + ParticleEmitter current = MolangParticleEngine.INSTANCE.getEmitter(cache.particlestorm$getCachedId().getInt(locator)); if (current == null || current.isRemoved() || !particle.equals(current.particleId)) { Vec3 pos = entity == null ? blockEntity.getBlockPos().getBottomCenter() : entity.position(); ParticleEmitter emitter = new ParticleEmitter(level, pos, particle, expression); - PSGameClient.LOADER.addEmitter(emitter, false); + MolangParticleEngine.INSTANCE.addEmitter(emitter, false); cache.particlestorm$getCachedId().put(locator, emitter.id); emitter.attachEntity(entity); emitter.attachedBlock = blockEntity; double[] offset = getLocatorOffset(locator); double[] rotation = getLocatorRotation(locator); - emitter.offsetPos = new Vec3(offset[0] * 0.0625, offset[1] * 0.0625, -offset[2] * 0.0625); - emitter.offsetRot = new Vector3f((float) Math.toRadians(rotation[0]), (float) Math.toRadians(rotation[1]), (float) Math.toRadians(rotation[2])); - Vector3f[] transform = cache.particlestorm$getTransform(bone); - emitter.parentPosition = transform[0]; - emitter.parentRotation = transform[1]; - emitter.parentMode = ParticleEmitter.ParentMode.LOCATOR; + LocatorState state = cache.particlestorm$getLocatorState(locator); + state.px = (float) (offset[0] * 0.0625); + state.py = (float) (offset[1] * 0.0625); + state.pz = (float) (offset[2] * 0.0625); + state.rx = (float) Math.toRadians(rotation[0]); + state.ry = (float) Math.toRadians(rotation[1]); + state.rz = (float) Math.toRadians(rotation[2]); } } return false; } - public static void setCurrentEntity(Object animatable, @Nullable Entity entity) { - if (animatable instanceof GeoWithCurrentEntity withCurrentEntity) { + public static void setCurrentEntity(GeoAnimatable animatable, @Nullable Entity entity) { + if (animatable instanceof ParticleStormGeoReplacedEntity withCurrentEntity) { withCurrentEntity.setCurrentEntity(entity); } } - public static void removeEmittersWhenAnimationChange(Object animationState, Object animatableInstanceCache) { + public static void removeEmittersWhenAnimationChange(AnimationController.State animationState, AnimatableInstanceCache animatableInstanceCache) { if (animationState == AnimationController.State.TRANSITIONING) { - IntIterator iterator = IAnimatableInstanceCache.of((AnimatableInstanceCache) animatableInstanceCache).particlestorm$getCachedId().values().iterator(); + IntIterator iterator = IAnimatableInstanceCache.of(animatableInstanceCache).particlestorm$getCachedId().values().iterator(); while (iterator.hasNext()) { - PSGameClient.LOADER.removeEmitter(iterator.nextInt(), false); + MolangParticleEngine.INSTANCE.removeEmitter(iterator.nextInt(), false); iterator.remove(); } } } + + private static final PoseStack poseStack = new PoseStack(); + private static final Quaternionf quaternion = new Quaternionf(); + + public static void transformLocator(GeoBone bone, GeoAnimatable animatable, float partialTick) { + Map locators = IGeoBone.of(bone).particlestorm$getLocators(); + if (locators == null || locators.isEmpty()) return; + poseStack.pushPose(); + RegisterLocatorPreTransformerEvent.getTransformer(animatable).transform(bone, animatable, poseStack, partialTick); + IAnimatableInstanceCache cache = IAnimatableInstanceCache.of(animatable.getAnimatableInstanceCache()); + for (LocatorValue locator : locators.values()) { + ParticleEmitter emitter = MolangParticleEngine.INSTANCE.getEmitter(cache.particlestorm$getCachedId().getInt(locator)); + if (emitter == null || emitter.isRemoved()) continue; + LocatorState state = cache.particlestorm$getLocatorState(locator); + poseStack.pushPose(); + poseStack.mulPose(quaternion.rotationXYZ(state.rx, state.ry, state.rz)); + poseStack.translate(-state.px, state.py, state.pz); + emitter.parentSpace = poseStack.last().pose(); + poseStack.popPose(); + } + poseStack.popPose(); + } + + public static class LocatorState { + float px, py, pz; + float rx, ry, rz; + } } diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/ParticleStormGeoReplacedEntity.java similarity index 79% rename from src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java rename to src/main/java/org/mesdag/particlestorm/api/geckolib/ParticleStormGeoReplacedEntity.java index 8960f9e..5e4cccb 100644 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/GeoWithCurrentEntity.java +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/ParticleStormGeoReplacedEntity.java @@ -4,7 +4,7 @@ import org.jetbrains.annotations.Nullable; import software.bernie.geckolib.animatable.GeoReplacedEntity; -public interface GeoWithCurrentEntity extends GeoReplacedEntity { +public interface ParticleStormGeoReplacedEntity extends GeoReplacedEntity { @Nullable Entity getCurrentEntity(); void setCurrentEntity(@Nullable Entity entity); diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/RegisterLocatorPreTransformerEvent.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/RegisterLocatorPreTransformerEvent.java new file mode 100644 index 0000000..e7d6aa4 --- /dev/null +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/RegisterLocatorPreTransformerEvent.java @@ -0,0 +1,147 @@ +package org.mesdag.particlestorm.api.geckolib; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import software.bernie.geckolib.animatable.GeoAnimatable; +import software.bernie.geckolib.animatable.GeoBlockEntity; +import software.bernie.geckolib.animatable.GeoEntity; +import software.bernie.geckolib.animatable.GeoItem; +import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; +import software.bernie.geckolib.cache.object.GeoBone; +import software.bernie.geckolib.util.RenderUtil; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class RegisterLocatorPreTransformerEvent extends Event implements IModBusEvent { + private static final Reference2ObjectMap> singletonTransformers = new Reference2ObjectOpenHashMap<>(); + private static final Reference2ObjectMap, Transformer> blockEntityTransformers = new Reference2ObjectOpenHashMap<>(); + private static final Reference2ObjectMap, Transformer> entityTransformers = new Reference2ObjectOpenHashMap<>(); + + RegisterLocatorPreTransformerEvent() {} + + public void register(T item, Transformer transformer) { + singletonTransformers.put(item.getAnimatableInstanceCache(), transformer); + } + + public void register(BlockEntityType blockEntity, Transformer transformer) { + blockEntityTransformers.put(blockEntity, transformer); + } + + public void register(EntityType entity, Transformer transformer) { + entityTransformers.put(entity, transformer); + } + + public void register(T replacedEntity, Transformer transformer) { + singletonTransformers.put(replacedEntity.getAnimatableInstanceCache(), transformer); + } + + @SuppressWarnings("unchecked") + public static Transformer getTransformer(T animatable) { + Transformer transformer = switch (animatable) { + case Entity entity -> entityTransformers.getOrDefault(entity.getType(), Transformer::entityTransformer); + case BlockEntity blockEntity -> blockEntityTransformers.getOrDefault(blockEntity.getType(), Transformer::defaultTransformer); + case Item ignored -> singletonTransformers.getOrDefault(animatable.getAnimatableInstanceCache(), Transformer::defaultTransformer); + case ParticleStormGeoReplacedEntity ignored -> singletonTransformers.getOrDefault(animatable.getAnimatableInstanceCache(), Transformer::replacedEntityTransformer); + default -> null; + }; + return transformer == null ? Transformer::defaultTransformer : (Transformer) transformer; + } + + @FunctionalInterface + public interface Transformer { + void transform(GeoBone bone, T animatable, PoseStack poseStack, float partialTick); + + static void replacedEntityTransformer(GeoBone bone, GeoAnimatable animatable, PoseStack poseStack, float partialTick) { + Entity entity = ((ParticleStormGeoReplacedEntity) animatable).getCurrentEntity(); + if (entity != null) { + transformEntity(entity, poseStack, partialTick); + } + defaultTransformer(bone, animatable, poseStack, partialTick); + } + + static void entityTransformer(GeoBone bone, GeoAnimatable animatable, PoseStack poseStack, float partialTick) { + transformEntity((Entity) animatable, poseStack, partialTick); + defaultTransformer(bone, animatable, poseStack, partialTick); + } + + /// [software.bernie.geckolib.renderer.GeoEntityRenderer#actuallyRender] + static void transformEntity(Entity entity, PoseStack poseStack, float partialTick) { + LivingEntity living = entity instanceof LivingEntity livingEntity ? livingEntity : null; + boolean shouldSit = entity.isPassenger() && (entity.getVehicle() != null); + float lerpBodyRot = living == null ? 0 : Mth.lerp(partialTick, living.yBodyRotO, living.yBodyRot); + float lerpHeadRot = living == null ? 0 : Mth.lerp(partialTick, living.yHeadRotO, living.yHeadRot); + + if (shouldSit && entity.getVehicle() instanceof LivingEntity livingEntity) { + lerpBodyRot = Mth.rotLerp(partialTick, livingEntity.yBodyRotO, livingEntity.yBodyRot); + float netHeadYaw = lerpHeadRot - lerpBodyRot; + float clampedHeadYaw = Mth.clamp(Mth.wrapDegrees(netHeadYaw), -85, 85); + lerpBodyRot = lerpHeadRot - clampedHeadYaw; + + if (clampedHeadYaw * clampedHeadYaw > 2500f) { + lerpBodyRot += clampedHeadYaw * 0.2f; + } + } + if (entity.getPose() == Pose.SLEEPING && living != null) { + Direction bedDirection = living.getBedOrientation(); + if (bedDirection != null) { + float eyePosOffset = living.getEyeHeight(Pose.STANDING) - 0.1F; + poseStack.translate(-bedDirection.getStepX() * eyePosOffset, 0, -bedDirection.getStepZ() * eyePosOffset); + } + } + float nativeScale = living != null ? living.getScale() : 1; + poseStack.scale(nativeScale, nativeScale, nativeScale); + if (entity.isFullyFrozen()) { + lerpBodyRot += (float) (Math.cos(entity.tickCount * 3.25d) * Math.PI * 0.4d); + } + if (!entity.hasPose(Pose.SLEEPING)) { + poseStack.mulPose(Axis.YP.rotationDegrees(180 - lerpBodyRot)); + } + if (living != null) { + if (living.deathTime > 0) { + float deathRotation = (living.deathTime + partialTick - 1f) / 20f * 1.6f; + poseStack.mulPose(Axis.ZP.rotationDegrees(Math.min(Mth.sqrt(deathRotation), 1) * 90)); + } else if (living.isAutoSpinAttack()) { + poseStack.mulPose(Axis.XP.rotationDegrees(-90f - living.getXRot())); + poseStack.mulPose(Axis.YP.rotationDegrees((living.tickCount + partialTick) * -75f)); + } else if (entity.hasPose(Pose.SLEEPING)) { + Direction bedOrientation = living.getBedOrientation(); + + poseStack.mulPose(Axis.YP.rotationDegrees(bedOrientation != null ? RenderUtil.getDirectionAngle(bedOrientation) : lerpBodyRot)); + poseStack.mulPose(Axis.ZP.rotationDegrees(90)); + poseStack.mulPose(Axis.YP.rotationDegrees(270f)); + } else if (LivingEntityRenderer.isEntityUpsideDown(living)) { + poseStack.translate(0, (entity.getBbHeight() + 0.1f) / nativeScale, 0); + poseStack.mulPose(Axis.ZP.rotationDegrees(180f)); + } + } + poseStack.translate(0, 0.01f, 0); + } + + static void defaultTransformer(GeoBone bone, GeoAnimatable animatable, PoseStack poseStack, float partialTick) { + Deque chain = new ArrayDeque<>(); + GeoBone current = bone; + while (current != null) { + chain.add(current); + current = current.getParent(); + } + while (!chain.isEmpty()) { + RenderUtil.prepMatrixForBone(poseStack, chain.pollLast()); + } + } + } +} diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperEntity.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperEntity.java index 21e4d9d..7924f5e 100644 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperEntity.java +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperEntity.java @@ -2,16 +2,21 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import org.jetbrains.annotations.Nullable; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.animation.AnimatableManager; import software.bernie.geckolib.animation.AnimationController; import software.bernie.geckolib.constant.DefaultAnimations; import software.bernie.geckolib.util.GeckoLibUtil; -public class ReplacedCreeperEntity implements GeoWithCurrentEntity { +public class ReplacedCreeperEntity implements ParticleStormGeoReplacedEntity { + public static final ReplacedCreeperEntity INSTANCE = new ReplacedCreeperEntity(); + private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this); private Entity currentEntity; + private ReplacedCreeperEntity() {} + public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { controllers.add(new AnimationController[]{DefaultAnimations.genericWalkIdleController(this)}); } @@ -30,7 +35,7 @@ public Entity getCurrentEntity() { } @Override - public void setCurrentEntity(Entity entity) { + public void setCurrentEntity(@Nullable Entity entity) { this.currentEntity = entity; } } diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperModel.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperModel.java deleted file mode 100644 index 07011f6..0000000 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperModel.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.mesdag.particlestorm.api.geckolib; - -import org.mesdag.particlestorm.ParticleStorm; -import software.bernie.geckolib.model.DefaultedEntityGeoModel; - -public class ReplacedCreeperModel extends DefaultedEntityGeoModel { - public ReplacedCreeperModel() { - super(ParticleStorm.asResource("creeper")); - } -} diff --git a/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperRenderer.java b/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperRenderer.java index 4361dac..3792141 100644 --- a/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperRenderer.java +++ b/src/main/java/org/mesdag/particlestorm/api/geckolib/ReplacedCreeperRenderer.java @@ -7,12 +7,14 @@ import net.minecraft.util.Mth; import net.minecraft.world.entity.monster.Creeper; import org.jetbrains.annotations.Nullable; +import org.mesdag.particlestorm.ParticleStorm; import software.bernie.geckolib.cache.object.BakedGeoModel; +import software.bernie.geckolib.model.DefaultedEntityGeoModel; import software.bernie.geckolib.renderer.GeoReplacedEntityRenderer; public class ReplacedCreeperRenderer extends GeoReplacedEntityRenderer { - public ReplacedCreeperRenderer(EntityRendererProvider.Context renderManager) { - super(renderManager, new ReplacedCreeperModel(), new ReplacedCreeperEntity()); + public ReplacedCreeperRenderer(EntityRendererProvider.Context context) { + super(context, new DefaultedEntityGeoModel<>(ParticleStorm.asResource("creeper")), ReplacedCreeperEntity.INSTANCE); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetime.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetime.java index e55eac0..1de62b7 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetime.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterLifetime.java @@ -2,11 +2,11 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.api.IEmitterComponent; import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.data.molang.FloatMolangExp; import org.mesdag.particlestorm.data.molang.MolangExp; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.ParticleEmitter; import java.util.List; @@ -130,7 +130,7 @@ public void update(ParticleEmitter emitter) { e.apply(emitter); } emitter.updateRandoms(emitter.level.random); - Queue queue = PSGameClient.LOADER.getParticlesForEmitter(emitter); + Queue queue = MolangParticleEngine.INSTANCE.getParticlesForEmitter(emitter); if (queue != null) { for (IMolangParticleInstance instance : queue) { FloatMolangExp exp = instance.getPreset().perUpdateExpression; diff --git a/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java b/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java index 64dec16..988e883 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/EmitterShape.java @@ -3,7 +3,6 @@ import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.client.Minecraft; import net.minecraft.client.particle.Particle; import net.minecraft.core.particles.ParticleGroup; import net.minecraft.util.Mth; @@ -14,18 +13,18 @@ import net.minecraft.world.phys.Vec3; import org.joml.Quaternionf; import org.joml.Vector3f; -import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.api.*; import org.mesdag.particlestorm.data.MathHelper; import org.mesdag.particlestorm.data.molang.FloatMolangExp; import org.mesdag.particlestorm.data.molang.FloatMolangExp3; import org.mesdag.particlestorm.data.molang.MolangExp; -import org.mesdag.particlestorm.particle.EmitterPreset; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.ParticleEmitter; import org.mesdag.particlestorm.particle.ParticlePreset; import java.util.Arrays; import java.util.List; +import java.util.Queue; public abstract sealed class EmitterShape implements IEmitterComponent permits EmitterShape.Disc, EmitterShape.Box, EmitterShape.EntityAABB, EmitterShape.Point, EmitterShape.Sphere { protected final boolean surfaceOnly; @@ -79,39 +78,21 @@ private void emittingParticle(Par component.apply(instance); } speed.mul(instance.getInitialSpeed()); - if (emitter.parentMode == ParticleEmitter.ParentMode.LOCATOR) { - position.x *= -1; - position.y *= -1; - speed.x *= -1; - speed.y *= -1; - } - EmitterPreset emitterPreset = emitter.getPreset(); - if (emitter.parentMode != ParticleEmitter.ParentMode.WORLD && emitterPreset.localPosition && !emitterPreset.localRotation) { - speed.x *= -1; - speed.z *= -1; - } speed.mul(emitter.invTickRate); - Entity attachedEntity = emitter.getAttachedEntity(); - if (attachedEntity == null) { - Vec3 emitterPos = emitter.getPosition(); - position.add((float) emitterPos.x, (float) emitterPos.y, (float) emitterPos.z); - } else { - // region todo 改为渲染时 -// if (emitter.parentMode == ParticleEmitter.ParentMode.LOCATOR) { -// -// } - if (emitterPreset.localRotation) { - MathHelper.applyEuler(emitter.rot.x, emitter.rot.y, 0.0F, position); - } - if (!emitterPreset.localPosition) { + + if (emitter.isLocalSpace()) { + if (!emitter.getPreset().localPosition) { Vec3 emitterPos = emitter.getPosition(); position.add((float) emitterPos.x, (float) emitterPos.y, (float) emitterPos.z); } - // endregion - if (emitterPreset.localVelocity) { + Entity attachedEntity = emitter.getAttachedEntity(); + if (attachedEntity != null && emitter.getPreset().localVelocity) { Vec3 emitterVec = attachedEntity.getDeltaMovement(); speed.add((float) emitterVec.x, (float) emitterVec.y, (float) emitterVec.z); } + } else { + Vec3 emitterPos = emitter.getPosition(); + position.add((float) emitterPos.x, (float) emitterPos.y, (float) emitterPos.z); } instance.setParticleSpeed(speed.x, speed.y, speed.z); @@ -127,13 +108,13 @@ private void emittingParticle(Par } instance.setComponents(particlePreset.effect.orderedParticleComponentsWhichRequireUpdate); if (!particlePreset.motionDynamic) instance.setParticleSpeed(0.0, 0.0, 0.0); - Minecraft.getInstance().particleEngine.add(instance); - PSGameClient.LOADER.addParticleForEmitter(instance); + MolangParticleEngine.INSTANCE.addParticle(instance); } private static boolean hasSpaceInParticleLimit(ParticleEmitter emitter) { ParticleGroup particleGroup = emitter.particleGroup; - return Minecraft.getInstance().particleEngine.trackedParticleCounts.getInt(particleGroup) < particleGroup.getLimit(); + Queue queue = MolangParticleEngine.INSTANCE.getParticlesForEmitter(emitter); + return queue == null || queue.size() < particleGroup.getLimit(); } /// This component spawns particles using a disc shape, particles can be spawned inside the shape or on its outer perimeter. diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfInBlocks.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfInBlocks.java index 87e92f4..182db16 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfInBlocks.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfInBlocks.java @@ -6,6 +6,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import org.joml.Vector3f; import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.IParticleComponent; import org.mesdag.particlestorm.data.molang.MolangExp; @@ -49,13 +50,26 @@ public void initialize(Level level) { } } + private static final Vector3f vector3f = new Vector3f(); + @Override public void apply(IMolangParticleInstance instance) { - if (!blocks.contains(instance.getLevel().getBlockState(BlockPos.containing(instance.getPosition())).getBlock())) { + instance.getEmitter().local2World(vector3f.set((float) instance.getX(), (float) instance.getY(), (float) instance.getZ()), 1); + if (!blocks.contains(instance.getLevel().getBlockState(BlockPos.containing(vector3f.x, vector3f.y, vector3f.z)).getBlock())) { instance.discard(); } } + @Override + public void update(IMolangParticleInstance instance) { + apply(instance); + } + + @Override + public boolean requireUpdate() { + return true; + } + @Override public String toString() { return "ParticleExpireIfInBlocks[blocks=" + ids + ']'; diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfNotInBlocks.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfNotInBlocks.java index 830c1ab..7b21440 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfNotInBlocks.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleExpireIfNotInBlocks.java @@ -6,6 +6,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import org.joml.Vector3f; import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.IParticleComponent; import org.mesdag.particlestorm.data.molang.MolangExp; @@ -46,13 +47,26 @@ public void initialize(Level level) { } } + private static final Vector3f vector3f = new Vector3f(); + @Override public void apply(IMolangParticleInstance instance) { - if (!blocks.contains(instance.getLevel().getBlockState(BlockPos.containing(instance.getPosition())).getBlock())) { + instance.getEmitter().local2World(vector3f.set((float) instance.getX(), (float) instance.getY(), (float) instance.getZ()), 1); + if (!blocks.contains(instance.getLevel().getBlockState(BlockPos.containing(vector3f.x, vector3f.y, vector3f.z)).getBlock())) { instance.discard(); } } + @Override + public void update(IMolangParticleInstance instance) { + apply(instance); + } + + @Override + public boolean requireUpdate() { + return true; + } + @Override public String toString() { return "ParticleExpireIfNotInBlocks[blocks=" + ids + ']'; diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java index 260faff..fff63db 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java @@ -76,6 +76,7 @@ public void apply(IMolangParticleInstance instance) { update(instance); instance.setCollisionDrag(collisionDrag * instance.getInvTickRate()); instance.setCoefficientOfRestitution(coefficientOfRestitution); + // todo fix bounding box float radius = Math.max(collisionRadius, Mth.EPSILON); instance.self().setBoundingBox(instance.self().getBoundingBox().inflate(radius, 0.0, radius)); instance.self().setLocationFromBoundingbox(); diff --git a/src/main/java/org/mesdag/particlestorm/data/event/NodeMolangExp.java b/src/main/java/org/mesdag/particlestorm/data/event/NodeMolangExp.java index 18999e1..d3fdd69 100644 --- a/src/main/java/org/mesdag/particlestorm/data/event/NodeMolangExp.java +++ b/src/main/java/org/mesdag/particlestorm/data/event/NodeMolangExp.java @@ -3,8 +3,11 @@ import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.world.phys.Vec3; +import org.joml.Vector3f; import org.mesdag.particlestorm.ParticleStorm; import org.mesdag.particlestorm.api.IEventNode; +import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.MolangExp; import org.mesdag.particlestorm.data.molang.compiler.MolangParser; @@ -31,6 +34,8 @@ public boolean shouldLog() { return log; } + private static final Vector3f vector3f = new Vector3f(); + @Override public void execute(MolangInstance instance) { if (variable == null && !expStr.isEmpty() && !expStr.isBlank()) { @@ -40,7 +45,13 @@ public void execute(MolangInstance instance) { if (variable != null) { double v = variable.get(instance); if (log) { - ParticleStorm.LOGGER.info("{}[{}]: {}={}", instance.getIdentity(), instance.getPosition(), expStr, v); + if (instance instanceof IMolangParticleInstance p) { + p.getEmitter().local2World(vector3f.set((float) p.getX(), (float) p.getY(), (float) p.getZ()), 1); + } else { + Vec3 pos = instance.getPosition(); + vector3f.set(pos.x, pos.y, pos.z); + } + ParticleStorm.LOGGER.info("{}[{},{},{}]: {}={}", instance.getIdentity(), vector3f.x, vector3f.y, vector3f.z, expStr, v); } } } diff --git a/src/main/java/org/mesdag/particlestorm/data/event/ParticleEffect.java b/src/main/java/org/mesdag/particlestorm/data/event/ParticleEffect.java index 8d50d1b..8437f48 100644 --- a/src/main/java/org/mesdag/particlestorm/data/event/ParticleEffect.java +++ b/src/main/java/org/mesdag/particlestorm/data/event/ParticleEffect.java @@ -9,12 +9,14 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.util.ByIdMap; import net.minecraft.util.StringRepresentable; -import org.mesdag.particlestorm.PSGameClient; +import net.minecraft.world.phys.Vec3; +import org.joml.Vector3f; import org.mesdag.particlestorm.api.IEventNode; import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.molang.MolangExp; import org.mesdag.particlestorm.data.molang.compiler.MolangQueries; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.ParticleEmitter; import java.util.List; @@ -36,14 +38,17 @@ public ParticleEffect(ResourceLocation effect, Type type, MolangExp preEffectExp this(effect, type, preEffectExpression, List.of()); } + private static final Vector3f vector3f = new Vector3f(); + @Override public void execute(MolangInstance instance) { ParticleEmitter emitter = new ParticleEmitter(instance.getEmitter(), this); - if (instance instanceof IMolangParticleInstance) { - emitter.setPos(instance.getPosition()); + if (instance instanceof IMolangParticleInstance p) { + p.getEmitter().local2World(vector3f.set((float) p.getX(), (float) p.getY(), (float) p.getZ()), 1); + emitter.setPos(new Vec3(vector3f.x, vector3f.y, vector3f.z)); emitter.posO = emitter.getPosition(); } - PSGameClient.LOADER.addEmitter(emitter, false); + MolangParticleEngine.INSTANCE.addEmitter(emitter, false); } @Override diff --git a/src/main/java/org/mesdag/particlestorm/data/event/SoundEffect.java b/src/main/java/org/mesdag/particlestorm/data/event/SoundEffect.java index 8b280cc..6b77c59 100644 --- a/src/main/java/org/mesdag/particlestorm/data/event/SoundEffect.java +++ b/src/main/java/org/mesdag/particlestorm/data/event/SoundEffect.java @@ -11,7 +11,9 @@ import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.phys.Vec3; +import org.joml.Vector3f; import org.mesdag.particlestorm.api.IEventNode; +import org.mesdag.particlestorm.api.IMolangParticleInstance; import org.mesdag.particlestorm.api.MolangInstance; public record SoundEffect(Holder soundEffect) implements IEventNode { @@ -22,9 +24,16 @@ public record SoundEffect(Holder soundEffect) implements IEventNode SOUND_EFFECT_CODEC.fieldOf("sound_effect").orElseGet(() -> Holder.direct(SoundEvents.EMPTY)).forGetter(SoundEffect::soundEffect) ).apply(instance, SoundEffect::new)); + private static final Vector3f vector3f = new Vector3f(); + @Override public void execute(MolangInstance instance) { - Vec3 position = instance.getPosition(); - instance.getLevel().playLocalSound(position.x, position.y, position.z, soundEffect.value(), SoundSource.AMBIENT, 1.0F, 1.0F, true); + if (instance instanceof IMolangParticleInstance p) { + p.getEmitter().local2World(vector3f.set((float) p.getX(), (float) p.getY(), (float) p.getZ()), 1); + } else { + Vec3 pos = instance.getPosition(); + vector3f.set(pos.x, pos.y, pos.z); + } + instance.getLevel().playLocalSound(vector3f.x, vector3f.y, vector3f.z, soundEffect.value(), SoundSource.AMBIENT, 1.0F, 1.0F, true); } } diff --git a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java index 6274970..24aba90 100644 --- a/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java +++ b/src/main/java/org/mesdag/particlestorm/data/molang/compiler/MolangQueries.java @@ -4,13 +4,12 @@ import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; import net.neoforged.fml.ModLoader; -import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.api.RegisterMolangQueriesEvent; import org.mesdag.particlestorm.api.ToFloatFunction; import org.mesdag.particlestorm.data.molang.compiler.value.Variable; +import org.mesdag.particlestorm.particle.MolangParticleEngine; -import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -84,8 +83,8 @@ private static void setDefaultQueryValues() { registerQueryVariable("query.player_level", p -> Minecraft.getInstance().player == null ? 0 : Minecraft.getInstance().player.experienceLevel); registerQueryVariable("query.time_of_day", p -> p.getLevel().getDayTime() / 24000f); registerQueryVariable("query.time_stamp", p -> p.getLevel().getGameTime()); - registerQueryVariable("query.total_emitter_count", p -> PSGameClient.LOADER.totalEmitterCount()); - registerQueryVariable("query.total_particle_count", p -> Minecraft.getInstance().particleEngine.particles.values().stream().mapToInt(Collection::size).sum()); + registerQueryVariable("query.total_emitter_count", p -> MolangParticleEngine.INSTANCE.totalEmitterCount()); + registerQueryVariable("query.total_particle_count", p -> MolangParticleEngine.INSTANCE.totalParticleCount()); registerQueryVariable("query.attached_x", p -> p.getAttachedEntity() == null ? 0 : (float) p.getAttachedEntity().getX()); registerQueryVariable("query.attached_y", p -> p.getAttachedEntity() == null ? 0 : (float) p.getAttachedEntity().getY()); registerQueryVariable("query.attached_z", p -> p.getAttachedEntity() == null ? 0 : (float) p.getAttachedEntity().getZ()); diff --git a/src/main/java/org/mesdag/particlestorm/mixed/IAnimatableInstanceCache.java b/src/main/java/org/mesdag/particlestorm/mixed/IAnimatableInstanceCache.java index 1ad1667..fa10cc9 100644 --- a/src/main/java/org/mesdag/particlestorm/mixed/IAnimatableInstanceCache.java +++ b/src/main/java/org/mesdag/particlestorm/mixed/IAnimatableInstanceCache.java @@ -1,16 +1,14 @@ package org.mesdag.particlestorm.mixed; import it.unimi.dsi.fastutil.objects.Object2IntMap; -import org.joml.Vector3f; +import org.mesdag.particlestorm.api.geckolib.GeckoLibHelper; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; -import software.bernie.geckolib.cache.object.GeoBone; import software.bernie.geckolib.loading.json.raw.LocatorValue; public interface IAnimatableInstanceCache { Object2IntMap particlestorm$getCachedId(); - /// \[position, rotation\] - Vector3f[] particlestorm$getTransform(GeoBone bone); + GeckoLibHelper.LocatorState particlestorm$getLocatorState(LocatorValue locator); static IAnimatableInstanceCache of(AnimatableInstanceCache cache) { return (IAnimatableInstanceCache) cache; diff --git a/src/main/java/org/mesdag/particlestorm/mixed/IGeoBone.java b/src/main/java/org/mesdag/particlestorm/mixed/IGeoBone.java index f35fbeb..903810c 100644 --- a/src/main/java/org/mesdag/particlestorm/mixed/IGeoBone.java +++ b/src/main/java/org/mesdag/particlestorm/mixed/IGeoBone.java @@ -1,12 +1,13 @@ package org.mesdag.particlestorm.mixed; +import org.jetbrains.annotations.Nullable; import software.bernie.geckolib.cache.object.GeoBone; import software.bernie.geckolib.loading.json.raw.LocatorValue; import java.util.Map; public interface IGeoBone { - Map particlestorm$getLocators(); + @Nullable Map particlestorm$getLocators(); void particlestorm$setLocators(Map locators); diff --git a/src/main/java/org/mesdag/particlestorm/mixed/IParticleKeyframeData.java b/src/main/java/org/mesdag/particlestorm/mixed/IParticleKeyframeData.java index 92fea2f..c0d2c0e 100644 --- a/src/main/java/org/mesdag/particlestorm/mixed/IParticleKeyframeData.java +++ b/src/main/java/org/mesdag/particlestorm/mixed/IParticleKeyframeData.java @@ -3,9 +3,14 @@ import net.minecraft.resources.ResourceLocation; import org.mesdag.particlestorm.data.molang.MolangExp; import org.mesdag.particlestorm.data.molang.VariableTable; +import software.bernie.geckolib.animation.keyframe.event.data.ParticleKeyframeData; public interface IParticleKeyframeData { ResourceLocation particlestorm$getParticle(); MolangExp particlestorm$getExpression(VariableTable variableTable); + + static IParticleKeyframeData of(ParticleKeyframeData data) { + return (IParticleKeyframeData) data; + } } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/LivingEntityMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/LivingEntityMixin.java index a244439..99c988a 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/LivingEntityMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/LivingEntityMixin.java @@ -5,7 +5,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; -import org.mesdag.particlestorm.PSGameClient; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.MolangParticleOption; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -15,7 +15,7 @@ public abstract class LivingEntityMixin { @WrapWithCondition(method = "tickEffects", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addParticle(Lnet/minecraft/core/particles/ParticleOptions;DDDDDD)V")) private boolean modify(Level instance, ParticleOptions particleData, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { if (instance.isClientSide && particleData instanceof MolangParticleOption(ResourceLocation id)) { - PSGameClient.LOADER.addTrackedEmitter((LivingEntity) (Object) this, id); + MolangParticleEngine.INSTANCE.addTrackedEmitter((LivingEntity) (Object) this, id); return false; } return true; diff --git a/src/main/java/org/mesdag/particlestorm/mixin/ParticleEngineMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/ParticleEngineMixin.java index fd793c3..b5c0019 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/ParticleEngineMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/ParticleEngineMixin.java @@ -1,18 +1,21 @@ package org.mesdag.particlestorm.mixin; import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.client.Camera; import net.minecraft.client.particle.ParticleEngine; import net.minecraft.client.particle.ParticleRenderType; -import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.texture.SpriteLoader; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.resources.ResourceLocation; -import org.mesdag.particlestorm.PSGameClient; +import org.jetbrains.annotations.Nullable; import org.mesdag.particlestorm.ParticleStorm; import org.mesdag.particlestorm.api.RegisterCustomParticleTypeEvent; import org.mesdag.particlestorm.data.DefinedParticleEffect; import org.mesdag.particlestorm.mixed.IParticleEngine; import org.mesdag.particlestorm.particle.ExtendMutableSpriteSet; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -23,7 +26,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.Map; -import java.util.function.Supplier; +import java.util.function.Predicate; @Mixin(ParticleEngine.class) public abstract class ParticleEngineMixin implements IParticleEngine { @@ -31,6 +34,9 @@ public abstract class ParticleEngineMixin implements IParticleEngine { @Final private Map spriteSets; + @Shadow + @Final + private TextureManager textureManager; @Unique private volatile SpriteLoader.Preparations particlestorm$preparations; @@ -39,7 +45,7 @@ public abstract class ParticleEngineMixin implements IParticleEngine { if (particlestorm$preparations != null && spriteSets.get(ParticleStorm.MOLANG.getId()) instanceof ExtendMutableSpriteSet spriteSet) { spriteSet.clear(); int i = 0; - for (Map.Entry entry : PSGameClient.LOADER.id2Effect().entrySet()) { + for (Map.Entry entry : MolangParticleEngine.INSTANCE.id2Effect().entrySet()) { TextureAtlasSprite missing = particlestorm$preparations.missing(); spriteSet.bindMissing(missing); ResourceLocation texture = entry.getValue().description.parameters().bindTexture(i); @@ -60,11 +66,15 @@ private SpriteLoader.Preparations cachePreparations(SpriteLoader.Preparations pr return this.particlestorm$preparations = preparations; } - @ModifyArg(method = "render(Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/Camera;FLnet/minecraft/client/renderer/culling/Frustum;Ljava/util/function/Predicate;)V", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;setShader(Ljava/util/function/Supplier;)V")) - private Supplier modify(Supplier original, @Local ParticleRenderType particlerendertype) { - if (!ParticleStorm.IRIS_LOADED && particlerendertype == PSGameClient.PARTICLE_BLEND) { - return PSGameClient::getParticleNoDiscardShader; - } - return original; + @Inject(method = "render(Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/Camera;FLnet/minecraft/client/renderer/culling/Frustum;Ljava/util/function/Predicate;)V", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;depthMask(Z)V")) + private void renderMolang( + CallbackInfo ci, + @Local(argsOnly = true) Camera camera, + @Local(argsOnly = true) float partialTick, + @Local(argsOnly = true) @Nullable Frustum frustum, + @Local(argsOnly = true) @Nullable Predicate renderTypePredicate + ) { + if (frustum == null || renderTypePredicate == null) return; + MolangParticleEngine.INSTANCE.renderParticles(textureManager, camera, partialTick, frustum, renderTypePredicate); } } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimatableInstanceCacheMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimatableInstanceCacheMixin.java index 11d5a3a..09f3b8f 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimatableInstanceCacheMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimatableInstanceCacheMixin.java @@ -3,12 +3,11 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import org.joml.Vector3f; +import org.mesdag.particlestorm.api.geckolib.GeckoLibHelper; import org.mesdag.particlestorm.mixed.IAnimatableInstanceCache; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.Unique; -import software.bernie.geckolib.cache.object.GeoBone; import software.bernie.geckolib.loading.json.raw.LocatorValue; import java.util.Map; @@ -19,7 +18,7 @@ public abstract class AnimatableInstanceCacheMixin implements IAnimatableInstanc @Unique private Object2IntMap particlestorm$cachedId; @Unique - private Map particlestorm$transform; + private Map particlestorm$transform; @Override public Object2IntMap particlestorm$getCachedId() { @@ -31,10 +30,10 @@ public abstract class AnimatableInstanceCacheMixin implements IAnimatableInstanc } @Override - public Vector3f[] particlestorm$getTransform(GeoBone bone) { + public GeckoLibHelper.LocatorState particlestorm$getLocatorState(LocatorValue locator) { if (particlestorm$transform == null) { this.particlestorm$transform = new Object2ObjectOpenHashMap<>(); } - return particlestorm$transform.computeIfAbsent(bone, b -> new Vector3f[]{new Vector3f(), new Vector3f()}); + return particlestorm$transform.computeIfAbsent(locator, b -> new GeckoLibHelper.LocatorState()); } } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationControllerMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationControllerMixin.java index 9c8a1a1..deca1da 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationControllerMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationControllerMixin.java @@ -51,7 +51,7 @@ public abstract class AnimationControllerMixin implemen @WrapWithCondition(method = "processCurrentAnimation", at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;log(Lorg/apache/logging/log4j/Level;Ljava/lang/String;)V", ordinal = 1)) private boolean processParticleEffect(Logger instance, Level level, String s, @Local(name = "keyframeData") ParticleKeyframeData keyframeData) { - return GeckoLibHelper.processParticleEffect(animatable, this, keyframeData); + return GeckoLibHelper.processParticleEffect(animatable, (AnimationController) (Object) this, keyframeData); } @Inject(method = "resetEventKeyFrames", at = @At("HEAD")) diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationProcessorMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationProcessorMixin.java index 3c80d79..8820f6c 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationProcessorMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/AnimationProcessorMixin.java @@ -9,11 +9,8 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import software.bernie.geckolib.animatable.GeoAnimatable; -import software.bernie.geckolib.animation.AnimatableManager; import software.bernie.geckolib.animation.AnimationController; -import software.bernie.geckolib.animation.AnimationState; import software.bernie.geckolib.cache.object.GeoBone; -import software.bernie.geckolib.model.GeoModel; import java.util.Collection; @@ -24,7 +21,7 @@ public abstract class AnimationProcessorMixin { public abstract Collection getRegisteredBones(); @Inject(method = "tickAnimation", at = @At(value = "INVOKE", target = "Lsoftware/bernie/geckolib/animation/AnimationController;process(Lsoftware/bernie/geckolib/model/GeoModel;Lsoftware/bernie/geckolib/animation/AnimationState;Ljava/util/Map;Ljava/util/Map;DZ)V")) - private void tickLocators(T animatable, GeoModel model, AnimatableManager animatableManager, double animTime, AnimationState state, boolean crashWhenCantFindBone, CallbackInfo ci, @Local AnimationController controller) { + private void fillLocators(CallbackInfo ci, @Local(name = "controller") AnimationController controller) { IAnimationController.of(controller).particlestorm$setBonesWhichHasLocators(getRegisteredBones()); } } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/BakedModelFactory$BuiltinMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/BakedModelFactory$BuiltinMixin.java index a8af9a3..708d444 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/BakedModelFactory$BuiltinMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/BakedModelFactory$BuiltinMixin.java @@ -1,5 +1,6 @@ package org.mesdag.particlestorm.mixin.integration.geckolib; +import com.llamalad7.mixinextras.sugar.Local; import org.mesdag.particlestorm.mixed.IGeoBone; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; @@ -7,14 +8,13 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import software.bernie.geckolib.cache.object.GeoBone; -import software.bernie.geckolib.loading.json.raw.ModelProperties; import software.bernie.geckolib.loading.object.BoneStructure; @Pseudo @Mixin(targets = "software.bernie.geckolib.loading.object.BakedModelFactory$Builtin", remap = false) public abstract class BakedModelFactory$BuiltinMixin { @Inject(method = "constructBone", at = @At("RETURN")) - private void addLocators(BoneStructure boneStructure, ModelProperties properties, GeoBone parent, CallbackInfoReturnable cir) { + private void addLocators(CallbackInfoReturnable cir, @Local(argsOnly = true) BoneStructure boneStructure) { IGeoBone.of(cir.getReturnValue()).particlestorm$setLocators(boneStructure.self().locators()); } } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoBoneMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoBoneMixin.java index 0a6fab3..c0af57d 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoBoneMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoBoneMixin.java @@ -1,42 +1,22 @@ package org.mesdag.particlestorm.mixin.integration.geckolib; -import org.joml.Vector3f; -import org.mesdag.particlestorm.mixed.IAnimatableInstanceCache; +import org.jetbrains.annotations.Nullable; import org.mesdag.particlestorm.mixed.IGeoBone; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; -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.callback.CallbackInfo; -import software.bernie.geckolib.animatable.GeoAnimatable; -import software.bernie.geckolib.cache.object.GeoBone; import software.bernie.geckolib.loading.json.raw.LocatorValue; -import software.bernie.geckolib.loading.math.MolangQueries; import java.util.Map; @Pseudo @Mixin(targets = "software.bernie.geckolib.cache.object.GeoBone", remap = false) public abstract class GeoBoneMixin implements IGeoBone { - @Shadow - private float positionX; - @Shadow - private float positionY; - @Shadow - private float positionZ; - @Shadow - private float rotX; - @Shadow - private float rotY; - @Shadow - private float rotZ; @Unique private Map particlestorm$locators; @Override - public Map particlestorm$getLocators() { + public @Nullable Map particlestorm$getLocators() { return particlestorm$locators; } @@ -44,17 +24,4 @@ public abstract class GeoBoneMixin implements IGeoBone { public void particlestorm$setLocators(Map locators) { this.particlestorm$locators = locators; } - - @Inject(method = "resetStateChanges", at = @At("TAIL")) - private void setData(CallbackInfo ci) { - if (particlestorm$locators == null || particlestorm$locators.isEmpty()) return; - MolangQueries.Actor actor = MolangQueriesAccessor.callGetActor(); - if (actor != null && actor.animatable() instanceof GeoAnimatable animatable) { - IAnimatableInstanceCache cache = IAnimatableInstanceCache.of(animatable.getAnimatableInstanceCache()); - Vector3f[] transform = cache.particlestorm$getTransform((GeoBone) (Object) this); - transform[0].set(positionX, positionY, positionZ); - transform[1].set(rotX, rotY, rotZ); - //cache.particlestorm$getScale().set(scaleX, scaleY, scaleZ); todo - } - } } diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoModelMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoModelMixin.java new file mode 100644 index 0000000..5ac9bea --- /dev/null +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoModelMixin.java @@ -0,0 +1,28 @@ +package org.mesdag.particlestorm.mixin.integration.geckolib; + +import com.llamalad7.mixinextras.sugar.Local; +import org.mesdag.particlestorm.api.geckolib.GeckoLibHelper; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import software.bernie.geckolib.animatable.GeoAnimatable; +import software.bernie.geckolib.animation.AnimationProcessor; +import software.bernie.geckolib.cache.object.GeoBone; + +@Pseudo +@Mixin(targets = "software.bernie.geckolib.model.GeoModel") +public abstract class GeoModelMixin { + @Inject(method = "handleAnimations", at = @At("TAIL")) + private void transform( + CallbackInfo ci, + @Local(argsOnly = true) T animatable, + @Local(argsOnly = true) float partialTick, + @Local(name = "processor") AnimationProcessor processor + ) { + for (GeoBone bone : processor.getRegisteredBones()) { + GeckoLibHelper.transformLocator(bone, animatable, partialTick); + } + } +} diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityMixin.java new file mode 100644 index 0000000..221caab --- /dev/null +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityMixin.java @@ -0,0 +1,10 @@ +package org.mesdag.particlestorm.mixin.integration.geckolib; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; + +@Pseudo +@Mixin(targets = "software.bernie.geckolib.animatable.GeoReplacedEntity") +public interface GeoReplacedEntityMixin { + +} diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityRendererMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityRendererMixin.java index 57ddd0f..d9816eb 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityRendererMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityRendererMixin.java @@ -1,7 +1,6 @@ package org.mesdag.particlestorm.mixin.integration.geckolib; -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.renderer.MultiBufferSource; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.world.entity.Entity; import org.mesdag.particlestorm.api.geckolib.GeckoLibHelper; import org.spongepowered.asm.mixin.Final; @@ -21,7 +20,7 @@ public abstract class GeoReplacedEntityRendererMixin PSGameClient.LOADER.totalEmitterCount()); - setActorVariable("query.total_particle_count", actor -> { - int sum = 0; - for (Integer value : Minecraft.getInstance().particleEngine.trackedParticleCounts.values()) { - sum += value; - } - return sum; - }); + setActorVariable("query.total_emitter_count", actor -> MolangParticleEngine.INSTANCE.totalEmitterCount()); + setActorVariable("query.total_particle_count", actor -> MolangParticleEngine.INSTANCE.totalParticleCount()); } } diff --git a/src/main/java/org/mesdag/particlestorm/network/EmitterAttachPacketS2C.java b/src/main/java/org/mesdag/particlestorm/network/EmitterAttachPacketS2C.java index 81fe611..1b69d6a 100644 --- a/src/main/java/org/mesdag/particlestorm/network/EmitterAttachPacketS2C.java +++ b/src/main/java/org/mesdag/particlestorm/network/EmitterAttachPacketS2C.java @@ -10,8 +10,8 @@ import net.minecraft.world.entity.player.Player; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.network.handling.IPayloadContext; -import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.ParticleStorm; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.ParticleEmitter; public record EmitterAttachPacketS2C(int particleId, int entityId) implements CustomPacketPayload { @@ -31,7 +31,7 @@ public void handle(IPayloadContext context) { context.enqueueWork(() -> { Player player = context.player(); if (player.isLocalPlayer()) { - ParticleEmitter emitter = PSGameClient.LOADER.getEmitter(particleId); + ParticleEmitter emitter = MolangParticleEngine.INSTANCE.getEmitter(particleId); Entity entity; if (emitter != null && (entity = player.level().getEntity(entityId)) != null) { emitter.attachEntity(entity); diff --git a/src/main/java/org/mesdag/particlestorm/network/EmitterCreationPacketS2C.java b/src/main/java/org/mesdag/particlestorm/network/EmitterCreationPacketS2C.java index d91be43..d89f935 100644 --- a/src/main/java/org/mesdag/particlestorm/network/EmitterCreationPacketS2C.java +++ b/src/main/java/org/mesdag/particlestorm/network/EmitterCreationPacketS2C.java @@ -15,9 +15,9 @@ import net.neoforged.neoforge.server.ServerLifecycleHooks; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; -import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.ParticleStorm; import org.mesdag.particlestorm.data.molang.MolangExp; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.ParticleEmitter; public record EmitterCreationPacketS2C(ResourceLocation id, Vector3f pos, MolangExp expression, int entityId) implements CustomPacketPayload { @@ -44,7 +44,7 @@ public void handle(IPayloadContext context) { if (entityId > 0) { emitter.attachEntity(player.level().getEntity(entityId)); } - PSGameClient.LOADER.addEmitter(emitter, false); + MolangParticleEngine.INSTANCE.addEmitter(emitter, false); } }).exceptionally(e -> { context.disconnect(Component.translatable("neoforge.network.invalid_flow", e.getMessage())); diff --git a/src/main/java/org/mesdag/particlestorm/network/EmitterRemovalPacket.java b/src/main/java/org/mesdag/particlestorm/network/EmitterRemovalPacket.java index 73219bd..1d1eca9 100644 --- a/src/main/java/org/mesdag/particlestorm/network/EmitterRemovalPacket.java +++ b/src/main/java/org/mesdag/particlestorm/network/EmitterRemovalPacket.java @@ -10,8 +10,8 @@ import net.minecraft.world.entity.player.Player; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.network.handling.IPayloadContext; -import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.ParticleStorm; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.ParticleEmitter; import static org.mesdag.particlestorm.network.EmitterSynchronizePacket.KEY; @@ -33,7 +33,7 @@ public void handle(IPayloadContext context) { context.enqueueWork(() -> { Player player = context.player(); if (player.isLocalPlayer()) { - ParticleEmitter emitter = PSGameClient.LOADER.removeEmitter(id, false); + ParticleEmitter emitter = MolangParticleEngine.INSTANCE.removeEmitter(id, false); if (emitter == null) { player.sendSystemMessage(Component.translatable("particle.notFound", id)); } else { diff --git a/src/main/java/org/mesdag/particlestorm/network/EmitterSynchronizePacket.java b/src/main/java/org/mesdag/particlestorm/network/EmitterSynchronizePacket.java index e6ed4b0..e905594 100644 --- a/src/main/java/org/mesdag/particlestorm/network/EmitterSynchronizePacket.java +++ b/src/main/java/org/mesdag/particlestorm/network/EmitterSynchronizePacket.java @@ -10,8 +10,8 @@ import net.minecraft.world.entity.player.Player; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.network.handling.IPayloadContext; -import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.ParticleStorm; +import org.mesdag.particlestorm.particle.MolangParticleEngine; import org.mesdag.particlestorm.particle.ParticleEmitter; public record EmitterSynchronizePacket(int id, CompoundTag tag) implements CustomPacketPayload { @@ -33,7 +33,7 @@ public void handle(IPayloadContext context) { context.enqueueWork(() -> { Player player = context.player(); if (player.isLocalPlayer()) { - PSGameClient.LOADER.loadEmitter(player.level(), id, tag); + MolangParticleEngine.INSTANCE.loadEmitter(player.level(), id, tag); } else { CompoundTag data = player.getPersistentData(); if (data.contains(KEY)) { diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleEngine.java similarity index 57% rename from src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java rename to src/main/java/org/mesdag/particlestorm/particle/MolangParticleEngine.java index 351ea3f..a924742 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleLoader.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleEngine.java @@ -1,17 +1,32 @@ package org.mesdag.particlestorm.particle; import com.google.common.collect.EvictingQueue; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.gson.JsonParseException; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.BufferUploader; +import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.serialization.JsonOps; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; import net.minecraft.Util; +import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.core.particles.ParticleGroup; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.FileToIdConverter; import net.minecraft.resources.ResourceLocation; @@ -25,6 +40,7 @@ import net.minecraft.world.level.Level; import net.neoforged.fml.ModLoader; import org.jetbrains.annotations.Nullable; +import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.ParticleStorm; import org.mesdag.particlestorm.api.*; import org.mesdag.particlestorm.data.DefinedParticleEffect; @@ -36,19 +52,25 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.function.Predicate; -public class MolangParticleLoader implements PreparableReloadListener { +public final class MolangParticleEngine implements PreparableReloadListener { + public static final MolangParticleEngine INSTANCE = new MolangParticleEngine(); private static final FileToIdConverter PARTICLE_LISTER = FileToIdConverter.json("particle_definitions"); - private Map id2Effect = new Hashtable<>(); - private Map id2Particle = new Hashtable<>(); - private Map id2Emitter = new Hashtable<>(); + private Map id2Effect = ImmutableMap.of(); + private Map id2Particle = ImmutableMap.of(); + private Map id2Emitter = ImmutableMap.of(); + private final Int2ObjectOpenHashMap emitters = new Int2ObjectOpenHashMap<>(); private final Object2ObjectOpenHashMap> tracker = new Object2ObjectOpenHashMap<>(); private final Int2ObjectOpenHashMap> particlesForEmitter = new Int2ObjectOpenHashMap<>(); + private final Queue particlesToAdd = new ArrayDeque<>(); + private final Reference2ObjectOpenHashMap> groupedParticles = new Reference2ObjectOpenHashMap<>(); private final IntAllocator allocator = new IntAllocator(); - private boolean initialized = false; + private MolangParticleEngine() {} + public Map id2Effect() { return id2Effect; } @@ -62,12 +84,12 @@ public Map id2Emitter() { } @SuppressWarnings("CallToPrintStackTrace") - public void tick(LocalPlayer player) { + public void tick(Minecraft minecraft, LocalPlayer player) { if (!initialized) { for (ParticlePreset detail : id2Particle.values()) { for (IComponent component : detail.effect.orderedComponents) { if (component instanceof IParticleComponent particleComponent) { - particleComponent.initialize(player.level()); + particleComponent.initialize(player.clientLevel); } } } @@ -75,37 +97,33 @@ public void tick(LocalPlayer player) { this.initialized = true; } if (!emitters.isEmpty()) { - int renderDistSqr = Mth.square(Minecraft.getInstance().options.renderDistance().get() * 16); - ObjectIterator> iterator = emitters.int2ObjectEntrySet().fastIterator(); + int renderDistSqr = Mth.square(minecraft.options.renderDistance().get() * 16); + var iterator = emitters.int2ObjectEntrySet().fastIterator(); while (iterator.hasNext()) { ParticleEmitter emitter = iterator.next().getValue(); try { - if (emitter.isRemoved() || emitter.level.dimension() != player.level().dimension()) { + if (emitter.isRemoved() || emitter.level.dimension() != player.clientLevel.dimension()) { emitter.onRemove(); - emitter.remove(); + removeEmitterNoUpdate(emitter); iterator.remove(); - allocator.release(emitter.id); - particlesForEmitter.remove(emitter.id); } else if (Mth.lengthSquared( emitter.getPosition().x - player.getX(), emitter.getPosition().z - player.getZ() ) < renderDistSqr) { emitter.tick(); } - } catch (Exception e) { - ParticleStorm.LOGGER.warn("Error ticking: {}", e.getMessage()); + } catch (Throwable e) { + ParticleStorm.LOGGER.warn("Error ticking emitter: {}", e.getMessage()); e.printStackTrace(); if (emitter != null) { - emitter.remove(); - allocator.release(emitter.id); - particlesForEmitter.remove(emitter.id); + removeEmitterNoUpdate(emitter); } iterator.remove(); } } } if (!tracker.isEmpty()) { - ObjectIterator>> iterator = tracker.object2ObjectEntrySet().fastIterator(); + var iterator = tracker.object2ObjectEntrySet().fastIterator(); while (iterator.hasNext()) { Map.Entry> entry = iterator.next(); if (entry.getKey().isRemoved()) { @@ -115,22 +133,85 @@ public void tick(LocalPlayer player) { } } } + if (!particlesToAdd.isEmpty()) { + for (IMolangParticleInstance instance : particlesToAdd) { + particlesForEmitter.computeIfAbsent(instance.getEmitter().id, i -> new ArrayDeque<>()).add(instance); + groupedParticles.computeIfAbsent(instance.self().getRenderType(), o -> new ArrayDeque<>()).add(instance); + } + particlesToAdd.clear(); + } + if (!groupedParticles.isEmpty()) { + var iterator = groupedParticles.reference2ObjectEntrySet().fastIterator(); + while (iterator.hasNext()) { + iterator.next().getValue().removeIf(instance -> { + try { + instance.self().tick(); + return instance.isDiscarded(); + } catch (Throwable e) { + ParticleStorm.LOGGER.warn("Error ticking particle: {}", e.getMessage()); + e.printStackTrace(); + instance.discard(); + return true; + } + }); + } + } if (!particlesForEmitter.isEmpty()) { - ObjectIterator>> iterator = particlesForEmitter.int2ObjectEntrySet().fastIterator(); + var iterator = particlesForEmitter.int2ObjectEntrySet().fastIterator(); while (iterator.hasNext()) { iterator.next().getValue().removeIf(IMolangParticleInstance::isDiscarded); } } } - public Iterable getEmitters() { - return emitters.values(); + public void renderParticles(TextureManager textureManager, Camera camera, float partialTick, Frustum frustum, Predicate renderTypePredicate) { + if (groupedParticles.isEmpty()) return; + Tesselator tesselator = Tesselator.getInstance(); + var iterator = groupedParticles.reference2ObjectEntrySet().fastIterator(); + while (iterator.hasNext()) { + var entry = iterator.next(); + ParticleRenderType type = entry.getKey(); + if (type == ParticleRenderType.NO_RENDER || !renderTypePredicate.test(type)) continue; + Queue queue = entry.getValue(); + if (queue.isEmpty()) continue; + + RenderSystem.setShader(!ParticleStorm.IRIS_LOADED && type == PSGameClient.PARTICLE_BLEND + ? PSGameClient::getParticleNoDiscardShader + : GameRenderer::getParticleShader); + BufferBuilder builder = type.begin(tesselator, textureManager); + if (builder == null) continue; + + for (IMolangParticleInstance instance : queue) { + if (instance.isVisible(camera, frustum, partialTick)) { + try { + instance.self().render(builder, camera, partialTick); + } catch (Throwable e) { + CrashReport report = CrashReport.forThrowable(e, "Rendering Molang Particle"); + CrashReportCategory category = report.addCategory("Molang Particle being rendered"); + category.setDetail("Particle Id", () -> instance.getEmitter().particleId.toString()); + throw new ReportedException(report); + } + } + } + MeshData mesh = builder.build(); + if (mesh != null) { + BufferUploader.drawWithShader(mesh); + } + } + } + + public ObjectIterator> getEmitters() { + return emitters.int2ObjectEntrySet().fastIterator(); } public int totalEmitterCount() { return emitters.size(); } + public int totalParticleCount() { + return particlesForEmitter.values().stream().mapToInt(Queue::size).sum(); + } + public void loadEmitter(Level level, int id, CompoundTag tag) { ParticleEmitter emitter = RegisterCustomEmitterTypeEvent.create(level, tag); emitter.id = id; @@ -160,14 +241,28 @@ public boolean addTrackedEmitter(Entity entity, ResourceLocation particleId) { return true; } - public void addParticleForEmitter(IMolangParticleInstance instance) { - particlesForEmitter.computeIfAbsent(instance.getEmitter().id, i -> new ArrayDeque<>()).add(instance); + public void addParticle(IMolangParticleInstance instance) { + Optional optional = instance.self().getParticleGroup(); + if (optional.isPresent()) { + Queue queue = particlesForEmitter.get(instance.getEmitter().id); + if (queue == null || queue.size() < optional.get().getLimit()) { + particlesToAdd.add(instance); + } + } else { + particlesToAdd.add(instance); + } } public @Nullable Queue getParticlesForEmitter(ParticleEmitter emitter) { return particlesForEmitter.get(emitter.id); } + private void removeEmitterNoUpdate(ParticleEmitter emitter) { + emitter.remove(); + allocator.release(emitter.id); + particlesForEmitter.remove(emitter.id); + } + public void removeEmitter(ParticleEmitter emitter, boolean sync) { removeEmitter(emitter.id, sync); } @@ -178,20 +273,23 @@ public void removeEmitter(ParticleEmitter emitter, boolean sync) { removed.onRemove(); } allocator.release(id); + particlesForEmitter.remove(id); if (sync) EmitterRemovalPacket.sendToServer(id); return removed; } public void removeAll() { if (!emitters.isEmpty()) { - ObjectIterator> iterator = emitters.int2ObjectEntrySet().iterator(); + ObjectIterator> iterator = emitters.int2ObjectEntrySet().fastIterator(); while (iterator.hasNext()) { iterator.next().getValue().remove(); iterator.remove(); } } - particlesForEmitter.clear(); tracker.clear(); + particlesForEmitter.clear(); + particlesToAdd.clear(); + groupedParticles.clear(); allocator.clear(); } @@ -220,9 +318,9 @@ public CompletableFuture reload(PreparationBarrier preparationBarrier, Res } return Util.sequence(list); }).thenCompose(preparationBarrier::wait).thenAcceptAsync(effects -> { - Map id2Effect = new Hashtable<>(); - Map id2Particle = new Hashtable<>(); - Map id2Emitter = new Hashtable<>(); + ImmutableMap.Builder id2Effect = ImmutableMap.builder(); + ImmutableMap.Builder id2Particle = ImmutableMap.builder(); + ImmutableMap.Builder id2Emitter = ImmutableMap.builder(); for (DefinedParticleEffect effect : effects) { ResourceLocation id = effect.description.identifier(); id2Effect.put(id, effect); @@ -233,9 +331,9 @@ public CompletableFuture reload(PreparationBarrier preparationBarrier, Res effect.events )); } - this.id2Effect = id2Effect; - this.id2Particle = id2Particle; - this.id2Emitter = id2Emitter; + this.id2Effect = id2Effect.build(); + this.id2Particle = id2Particle.build(); + this.id2Emitter = id2Emitter.build(); this.initialized = false; ModLoader.postEvent(new MolangParticleLoadEvent.Post(gameExecutor)); }, gameExecutor); diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java index 894ca96..9f7f352 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java @@ -5,6 +5,7 @@ import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.TextureSheetParticle; +import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.particles.ParticleGroup; import net.minecraft.util.Mth; @@ -389,7 +390,39 @@ public void tick() { } protected static final Quaternionf quaternionf = new Quaternionf(); + protected static final Vector3f vector3f = new Vector3f(); + + // 在render前调用 + @Override + public boolean isVisible(Camera camera, Frustum frustum, float partialTick) { + Vec3 camPos = camera.getPosition(); + if (emitter.isLocalSpace()) { + emitter.local2World(vector3f.set( + (float) Mth.lerp(partialTick, xo, x), + (float) Mth.lerp(partialTick, yo, y), + (float) Mth.lerp(partialTick, zo, z) + ), partialTick); + float size = Math.max(billboardSize[0], billboardSize[1]); + boolean inFrustum = frustum.cubeInFrustum( + vector3f.x - size, + vector3f.y - size, + vector3f.z - size, + vector3f.x + size, + vector3f.y + size, + vector3f.z + size + ); + vector3f.sub((float) camPos.x, (float) camPos.y, (float) camPos.z); + return inFrustum; + } + vector3f.set( + (float) (Mth.lerp(partialTick, xo, x) - camPos.x), + (float) (Mth.lerp(partialTick, yo, y) - camPos.y), + (float) (Mth.lerp(partialTick, zo, z) - camPos.z) + ); + return IMolangParticleInstance.super.isVisible(camera, frustum, partialTick); + } + // 在isVisible后调用 @Override public void render(VertexConsumer buffer, Camera camera, float partialTicks) { quaternionf.identity(); @@ -397,7 +430,7 @@ public void render(VertexConsumer buffer, Camera camera, float partialTicks) { if (xRot != 0.0F) quaternionf.rotateX(Mth.lerp(partialTicks, xRotO, xRot)); if (yRot != 0.0F) quaternionf.rotateY(Mth.lerp(partialTicks, yRotO, yRot)); if (roll != 0.0F) quaternionf.rotateZ(Mth.lerp(partialTicks, oRoll, roll)); - renderRotatedQuad(buffer, camera, quaternionf, partialTicks); + renderRotatedQuad(buffer, quaternionf, vector3f.x, vector3f.y, vector3f.z, partialTicks); } @Override @@ -417,8 +450,6 @@ protected void renderRotatedQuad(VertexConsumer buffer, Quaternionf quaternion, } } - protected static final Vector3f vector3f = new Vector3f(); - @Override protected void renderVertex(VertexConsumer buffer, Quaternionf quaternion, float x, float y, float z, float xOffset, float yOffset, float quadSize, float u, float v, int packedLight) { vector3f.set(xOffset * billboardSize[0], yOffset * billboardSize[1], 0.0F).rotate(quaternion).add(x, y, z); @@ -434,10 +465,13 @@ public AABB getRenderBoundingBox(float partialTicks) { @Override public void move(double x, double y, double z) { if (stoppedByCollision) return; + + // todo 坐标系转换 + double d0 = x; double d1 = y; double d2 = z; - if (hasPhysics && hasCollision && (x != 0.0 || y != 0.0 || z != 0.0) && x * x + y * y + z * z < MAXIMUM_COLLISION_VELOCITY_SQUARED) { + if (hasPhysics && hasCollision && (x != 0.0 || y != 0.0 || z != 0.0) && Mth.lengthSquared(x, y, z) < MAXIMUM_COLLISION_VELOCITY_SQUARED) { Vec3 vec3 = Entity.collideBoundingBox(null, new Vec3(x, y, z), getBoundingBox(), level, List.of()); if (x != vec3.x) { this.xd = -Mth.sign(xd) * (Math.abs(xd) - collisionDrag) * coefficientOfRestitution; @@ -463,13 +497,12 @@ public void move(double x, double y, double z) { if (hasPhysics && hasCollision) { this.onGround = d1 != y && d1 < 0.0; - boolean collided = d0 != x || d2 != z; - if (onGround || collided) { + if (onGround || (d0 != x || d2 != z)) { if (!preset.collisionEvents.isEmpty()) { for (ParticleMotionCollision.Event event : preset.collisionEvents) { float tickSpeed = event.minSpeed() * getInvTickRate(); - if (tickSpeed * tickSpeed < xd * xd + yd * yd + zd * zd) { + if (tickSpeed * tickSpeed < Mth.lengthSquared(xd, yd, zd)) { for (IEventNode node : preset.effect.events.get(event.event()).values()) { node.execute(this); } diff --git a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java index a88c806..32821ab 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java +++ b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java @@ -6,14 +6,13 @@ import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; import org.joml.Vector3f; -import org.mesdag.particlestorm.PSGameClient; import org.mesdag.particlestorm.api.IEmitterComponent; import org.mesdag.particlestorm.api.MolangInstance; import org.mesdag.particlestorm.data.MathHelper; @@ -35,11 +34,8 @@ public class ParticleEmitter implements MolangInstance { public ResourceLocation particleId; public MolangExp expression; - public transient ParentMode parentMode = ParentMode.WORLD; - public transient Vec3 offsetPos = Vec3.ZERO; - public transient Vector3f offsetRot = new Vector3f(); - public transient Vector3f parentPosition; - public transient Vector3f parentRotation; + public transient Matrix4f parentSpace; + protected transient EmitterPreset preset; protected transient VariableTable vars; protected transient List components; @@ -77,7 +73,7 @@ public class ParticleEmitter implements MolangInstance { public transient final Level level; protected Vec3 pos; public Vec3 posO = Vec3.ZERO; - public Vector3f rot = new Vector3f(); + protected final Vector3f rotated = new Vector3f(); public boolean hideOutline; private transient boolean removed = false; @@ -117,11 +113,7 @@ public ParticleEmitter(ParticleEmitter parent, ParticleEffect effect) { case EMITTER_BOUND -> { attachEntity(parent.getAttachedEntity()); this.attachedBlock = parent.attachedBlock; - this.offsetPos = parent.offsetPos; - this.offsetRot = parent.offsetRot; - this.parentPosition = parent.parentPosition; - this.parentRotation = parent.parentRotation; - this.parentMode = parent.parentMode; + this.parentSpace = parent.parentSpace; } case PARTICLE -> this.isManual = true; case PARTICLE_WITH_VELOCITY -> { @@ -162,7 +154,7 @@ protected void init() { } protected void createVars() { - this.preset = PSGameClient.LOADER.id2Emitter().get(particleId); + this.preset = MolangParticleEngine.INSTANCE.id2Emitter().get(particleId); if (preset == null) { throw new IllegalArgumentException("Unknown particle id: '" + particleId + "'!"); } @@ -216,12 +208,8 @@ public void tick() { remove(); return; } - if (parentRotation != null) { - rot.set(parentRotation).add(offsetRot.x, offsetRot.y + getAttachedYRot() * Mth.DEG_TO_RAD, offsetRot.z); - } - Vector3f rotated = offsetPos.toVector3f().rotateZ(rot.z).rotateY(rot.y).rotateX(rot.x); - if (parentPosition != null) { - rotated.add(parentPosition); + if (parentSpace != null) { + rotated.set(parentSpace.m30(), parentSpace.m31(), parentSpace.m32()); } this.pos = new Vec3(attached.getX() + rotated.x, attached.getY() + rotated.y, attached.getZ() + rotated.z); } else if (attachedBlock != null) { @@ -229,15 +217,11 @@ public void tick() { remove(); return; } - if (parentRotation != null) { - rot.set(parentRotation).add(offsetRot); - } - Vector3f rotated = offsetPos.toVector3f().rotateZ(rot.z).rotateY(rot.y).rotateX(rot.x); - if (parentPosition != null) { - rotated.add(parentPosition); + if (parentSpace != null) { + rotated.set(parentSpace.m30(), parentSpace.m31(), parentSpace.m32()); } - BlockPos pos1 = attachedBlock.getBlockPos(); - this.pos = new Vec3(pos1.getX() + 0.5 + rotated.x, pos1.getY() + rotated.y, pos1.getZ() + 0.5 + rotated.z); + BlockPos bp = attachedBlock.getBlockPos(); + this.pos = new Vec3(bp.getX() + 0.5 + rotated.x, bp.getY() + rotated.y, bp.getZ() + 0.5 + rotated.z); } if (afterParentInit != null && parent != null) { @@ -252,8 +236,25 @@ public void tick() { } } - private float getAttachedYRot() { - return attached instanceof LivingEntity living ? -living.yBodyRot : attached.getYRot(); + public void local2World(Vector3f vec, float partialTick) { + if (isLocalSpace()) { + if (preset.localRotation) { + if (parentSpace != null) { + vec.mulDirection(parentSpace); + } + } + if (preset.localPosition) { + vec.add( + (float) Mth.lerp(partialTick, posO.x, pos.x), + (float) Mth.lerp(partialTick, posO.y, pos.y), + (float) Mth.lerp(partialTick, posO.z, pos.z) + ); + } + } + } + + public boolean isLocalSpace() { + return parentSpace != null; } public void remove() { @@ -306,7 +307,6 @@ public void deserialize(CompoundTag tag) { this.emitterRandom3 = tag.getFloat("emitterRandom3"); this.emitterRandom4 = tag.getFloat("emitterRandom4"); this.posO = this.pos = new Vec3(tag.getDouble("posX"), tag.getDouble("posY"), tag.getDouble("posZ")); - this.rot.set(tag.getFloat("rotX"), tag.getFloat("rotY"), tag.getFloat("rotZ")); this.hideOutline = tag.getBoolean("hideOutline"); } @@ -320,9 +320,6 @@ public void serialize(CompoundTag tag) { tag.putDouble("posX", pos.x); tag.putDouble("posY", pos.y); tag.putDouble("posZ", pos.z); - tag.putFloat("rotX", rot.x); - tag.putFloat("rotY", rot.y); - tag.putFloat("rotZ", rot.z); tag.putBoolean("hideOutline", hideOutline); } @@ -402,9 +399,4 @@ public float getInvTickRate() { public ParticleEmitter getEmitter() { return this; } - - public enum ParentMode { - LOCATOR, - WORLD - } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 083a960..b255a7d 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -5,5 +5,5 @@ public net.minecraft.client.particle.Particle setLocationFromBoundingbox()V public net.minecraft.client.particle.ParticleEngine$MutableSpriteSet protected net.minecraft.client.particle.ParticleEngine$MutableSpriteSet sprites protected net.minecraft.client.particle.ParticleEngine$MutableSpriteSet ()V -public net.minecraft.client.particle.ParticleEngine trackedParticleCounts public net.minecraft.client.particle.ParticleEngine particles +public net.minecraft.client.renderer.culling.Frustum cubeInFrustum(DDDDDD)Z diff --git a/src/main/resources/assets/particlestorm/animations/block/test_block.animation.json b/src/main/resources/assets/particlestorm/animations/block/test_block.animation.json index 79ba2ec..b9dba1e 100644 --- a/src/main/resources/assets/particlestorm/animations/block/test_block.animation.json +++ b/src/main/resources/assets/particlestorm/animations/block/test_block.animation.json @@ -3,12 +3,13 @@ "animations": { "animation.test_block.new": { "loop": true, - "animation_length": 2, + "animation_length": 4, "bones": { "bone": { - "rotation": { - "vector": [0, "math.sin(query.anim_time * 90) * 180", 0] - } + "rotation": [0, "-query.anim_time * 90", 0] + }, + "bone2": { + "rotation": [0, "-query.anim_time * 90", 0] } }, "particle_effects": { diff --git a/src/main/resources/assets/particlestorm/geo/block/test_block.geo.json b/src/main/resources/assets/particlestorm/geo/block/test_block.geo.json index d5b21b5..33f5a80 100644 --- a/src/main/resources/assets/particlestorm/geo/block/test_block.geo.json +++ b/src/main/resources/assets/particlestorm/geo/block/test_block.geo.json @@ -47,8 +47,29 @@ } ], "locators": { - "locator": [-3, 6, -3], - "locator2": [3, 12, 3] + "locator": [-3, 6, -3] + } + }, + { + "name": "bone2", + "parent": "bone", + "pivot": [0, 0, -8], + "cubes": [ + { + "origin": [-1, 0, -15], + "size": [2, 2, 2], + "uv": { + "north": {"uv": [0, 0], "uv_size": [2, 2]}, + "east": {"uv": [0, 0], "uv_size": [2, 2]}, + "south": {"uv": [0, 0], "uv_size": [2, 2]}, + "west": {"uv": [0, 0], "uv_size": [2, 2]}, + "up": {"uv": [2, 2], "uv_size": [-2, -2]}, + "down": {"uv": [2, 2], "uv_size": [-2, -2]} + } + } + ], + "locators": { + "locator2": [0, 1, -14] } } ] diff --git a/src/main/resources/assets/particlestorm/particle_definitions/blend.json b/src/main/resources/assets/particlestorm/particle_definitions/blend.json index afb83cb..2710a9b 100644 --- a/src/main/resources/assets/particlestorm/particle_definitions/blend.json +++ b/src/main/resources/assets/particlestorm/particle_definitions/blend.json @@ -9,10 +9,6 @@ } }, "components": { - "minecraft:emitter_local_space": { - "position": true, - "rotation": false - }, "minecraft:emitter_rate_steady": { "spawn_rate": 10, "max_particles": 100 diff --git a/src/main/resources/assets/particlestorm/particle_definitions/loading.particle.json b/src/main/resources/assets/particlestorm/particle_definitions/loading.particle.json index 539d849..e76999b 100644 --- a/src/main/resources/assets/particlestorm/particle_definitions/loading.particle.json +++ b/src/main/resources/assets/particlestorm/particle_definitions/loading.particle.json @@ -21,15 +21,16 @@ "max_particles": 60 }, "minecraft:emitter_lifetime_looping": { - "active_time": 1 + "active_time": 10 }, "minecraft:emitter_shape_point": { - "offset": ["variable.radius*-math.sin(variable.emitter_age*360)", "variable.radius*math.cos(variable.emitter_age*360)", 0] + "offset": ["variable.radius*-math.sin(variable.emitter_age*360)", "variable.radius*math.cos(variable.emitter_age*360)", 0], + "direction": [0, 0, 1] }, "minecraft:particle_lifetime_expression": { "max_lifetime": 1 }, - "minecraft:particle_initial_speed": 0, + "minecraft:particle_initial_speed": 5, "minecraft:particle_motion_dynamic": {}, "minecraft:particle_appearance_billboard": { "size": ["variable.size*(1-variable.particle_age)", "variable.size*(1-variable.particle_age)"], diff --git a/src/main/resources/particlestorm.mixins.json b/src/main/resources/particlestorm.mixins.json index e582d26..30bff53 100644 --- a/src/main/resources/particlestorm.mixins.json +++ b/src/main/resources/particlestorm.mixins.json @@ -4,7 +4,10 @@ "package": "org.mesdag.particlestorm.mixin", "compatibilityLevel": "JAVA_21", "refmap": "particlestorm.refmap.json", - "mixins": [], + "mixins": [ + "integration.geckolib.GeoModelMixin", + "integration.geckolib.GeoReplacedEntityMixin" + ], "client": [ "BlockEntityMixin", "EntityMixin", From 9e1c560c08d55810f69066c010f0825756f44954 Mon Sep 17 00:00:00 2001 From: westernat Date: Tue, 26 May 2026 20:10:03 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99mixin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mixin/integration/geckolib/GeoModelMixin.java | 2 +- .../geckolib/GeoReplacedEntityMixin.java | 10 ---------- .../integration/geckolib/MolangQueriesAccessor.java | 13 ------------- src/main/resources/particlestorm.mixins.json | 1 - 4 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityMixin.java delete mode 100644 src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/MolangQueriesAccessor.java diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoModelMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoModelMixin.java index 5ac9bea..428a17e 100644 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoModelMixin.java +++ b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoModelMixin.java @@ -12,7 +12,7 @@ import software.bernie.geckolib.cache.object.GeoBone; @Pseudo -@Mixin(targets = "software.bernie.geckolib.model.GeoModel") +@Mixin(targets = "software.bernie.geckolib.model.GeoModel", remap = false) public abstract class GeoModelMixin { @Inject(method = "handleAnimations", at = @At("TAIL")) private void transform( diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityMixin.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityMixin.java deleted file mode 100644 index 221caab..0000000 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/GeoReplacedEntityMixin.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.mesdag.particlestorm.mixin.integration.geckolib; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Pseudo; - -@Pseudo -@Mixin(targets = "software.bernie.geckolib.animatable.GeoReplacedEntity") -public interface GeoReplacedEntityMixin { - -} diff --git a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/MolangQueriesAccessor.java b/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/MolangQueriesAccessor.java deleted file mode 100644 index d3e1f6a..0000000 --- a/src/main/java/org/mesdag/particlestorm/mixin/integration/geckolib/MolangQueriesAccessor.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.mesdag.particlestorm.mixin.integration.geckolib; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Pseudo; -import org.spongepowered.asm.mixin.gen.Invoker; -import software.bernie.geckolib.loading.math.MolangQueries; - -@Pseudo -@Mixin(targets = "software.bernie.geckolib.loading.math.MolangQueries") -public interface MolangQueriesAccessor { - @Invoker - static MolangQueries.Actor callGetActor() {throw new UnsupportedOperationException();} -} diff --git a/src/main/resources/particlestorm.mixins.json b/src/main/resources/particlestorm.mixins.json index 30bff53..7285556 100644 --- a/src/main/resources/particlestorm.mixins.json +++ b/src/main/resources/particlestorm.mixins.json @@ -21,7 +21,6 @@ "integration.geckolib.BakedModelFactory$BuiltinMixin", "integration.geckolib.GeoBoneMixin", "integration.geckolib.GeoReplacedEntityRendererMixin", - "integration.geckolib.MolangQueriesAccessor", "integration.geckolib.MolangQueriesMixin", "integration.geckolib.ParticleKeyframeDataMixin" ], From a64f86ad04b5138bba668c6e04222e94123152f1 Mon Sep 17 00:00:00 2001 From: westernat Date: Tue, 26 May 2026 20:39:01 +0800 Subject: [PATCH 13/13] collision radius --- .../api/IMolangParticleInstance.java | 16 ++++++++++++- .../component/ParticleMotionCollision.java | 6 ++--- .../particle/MolangParticleInstance.java | 24 ++++++++++++++++--- .../particle/ParticleEmitter.java | 8 +++++-- src/main/resources/particlestorm.mixins.json | 3 +-- 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java b/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java index cc38b37..7e7cf0c 100644 --- a/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java +++ b/src/main/java/org/mesdag/particlestorm/api/IMolangParticleInstance.java @@ -7,6 +7,7 @@ import net.minecraft.core.particles.ParticleGroup; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; @@ -50,6 +51,10 @@ default Particle self() { void setExpireOnContact(boolean b); + void setCollisionRadius(float radius); + + float getCollisionRadius(); + void setComponents(List components); float getScaleU(); @@ -110,7 +115,16 @@ default Particle self() { // region default default void moveDirectly(double x, double y, double z) { - self().setBoundingBox(self().getBoundingBox().move(x, y, z)); + AABB aabb = self().getBoundingBox(); + float radius = getCollisionRadius(); + self().setBoundingBox(new AABB( + aabb.minX - radius + x, + aabb.minY - radius + y, + aabb.minZ - radius + z, + aabb.maxX + radius + x, + aabb.maxY + radius + y, + aabb.maxZ + radius + z + )); self().setLocationFromBoundingbox(); } diff --git a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java index fff63db..8bca800 100644 --- a/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java +++ b/src/main/java/org/mesdag/particlestorm/data/component/ParticleMotionCollision.java @@ -76,10 +76,8 @@ public void apply(IMolangParticleInstance instance) { update(instance); instance.setCollisionDrag(collisionDrag * instance.getInvTickRate()); instance.setCoefficientOfRestitution(coefficientOfRestitution); - // todo fix bounding box - float radius = Math.max(collisionRadius, Mth.EPSILON); - instance.self().setBoundingBox(instance.self().getBoundingBox().inflate(radius, 0.0, radius)); - instance.self().setLocationFromBoundingbox(); + instance.setCollisionRadius(Math.max(collisionRadius, Mth.EPSILON)); + instance.moveDirectly(0, 0, 0); instance.setExpireOnContact(expireOnContact); } diff --git a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java index 9f7f352..5ada2e2 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java +++ b/src/main/java/org/mesdag/particlestorm/particle/MolangParticleInstance.java @@ -69,6 +69,7 @@ public class MolangParticleInstance extends TextureSheetParticle implements IMol public MolangParticleInstance(ParticlePreset preset, ClientLevel level, double x, double y, double z, ExtendMutableSpriteSet sprites) { super(level, x, y, z); this.friction = 1.0F; + this.quadSize = 0; // as collision radius this.preset = preset; setSprite(sprites.get(preset.effect.description.parameters().getTextureIndex())); this.originX = ((ITextureAtlasSprite) sprite).particlestorm$getOriginX(); @@ -159,6 +160,16 @@ public void setExpireOnContact(boolean b) { this.expireOnContact = b; } + @Override + public void setCollisionRadius(float radius) { + this.quadSize = radius; + } + + @Override + public float getCollisionRadius() { + return quadSize; + } + @Override public void setComponents(List components) { this.components = components; @@ -466,13 +477,20 @@ public AABB getRenderBoundingBox(float partialTicks) { public void move(double x, double y, double z) { if (stoppedByCollision) return; - // todo 坐标系转换 - double d0 = x; double d1 = y; double d2 = z; if (hasPhysics && hasCollision && (x != 0.0 || y != 0.0 || z != 0.0) && Mth.lengthSquared(x, y, z) < MAXIMUM_COLLISION_VELOCITY_SQUARED) { - Vec3 vec3 = Entity.collideBoundingBox(null, new Vec3(x, y, z), getBoundingBox(), level, List.of()); + AABB aabb = getBoundingBox(); + if (emitter.isLocalSpace()) { + emitter.local2World(vector3f.set(aabb.minX, aabb.minY, aabb.minZ), 1); + float mx = vector3f.x; + float my = vector3f.y; + float mz = vector3f.z; + emitter.local2World(vector3f.set(aabb.maxX, aabb.maxY, aabb.maxZ), 1); + aabb = new AABB(mx, my, mz, vector3f.x, vector3f.y, vector3f.z); + } + Vec3 vec3 = Entity.collideBoundingBox(null, new Vec3(x, y, z), aabb, level, List.of()); if (x != vec3.x) { this.xd = -Mth.sign(xd) * (Math.abs(xd) - collisionDrag) * coefficientOfRestitution; } diff --git a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java index 32821ab..5f2bc8f 100644 --- a/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java +++ b/src/main/java/org/mesdag/particlestorm/particle/ParticleEmitter.java @@ -208,8 +208,10 @@ public void tick() { remove(); return; } - if (parentSpace != null) { + if (isLocalSpace()) { rotated.set(parentSpace.m30(), parentSpace.m31(), parentSpace.m32()); + } else { + rotated.set(0); } this.pos = new Vec3(attached.getX() + rotated.x, attached.getY() + rotated.y, attached.getZ() + rotated.z); } else if (attachedBlock != null) { @@ -217,8 +219,10 @@ public void tick() { remove(); return; } - if (parentSpace != null) { + if (isLocalSpace()) { rotated.set(parentSpace.m30(), parentSpace.m31(), parentSpace.m32()); + } else { + rotated.set(0); } BlockPos bp = attachedBlock.getBlockPos(); this.pos = new Vec3(bp.getX() + 0.5 + rotated.x, bp.getY() + rotated.y, bp.getZ() + 0.5 + rotated.z); diff --git a/src/main/resources/particlestorm.mixins.json b/src/main/resources/particlestorm.mixins.json index 7285556..288ca50 100644 --- a/src/main/resources/particlestorm.mixins.json +++ b/src/main/resources/particlestorm.mixins.json @@ -5,8 +5,6 @@ "compatibilityLevel": "JAVA_21", "refmap": "particlestorm.refmap.json", "mixins": [ - "integration.geckolib.GeoModelMixin", - "integration.geckolib.GeoReplacedEntityMixin" ], "client": [ "BlockEntityMixin", @@ -20,6 +18,7 @@ "integration.geckolib.AnimationProcessorMixin", "integration.geckolib.BakedModelFactory$BuiltinMixin", "integration.geckolib.GeoBoneMixin", + "integration.geckolib.GeoModelMixin", "integration.geckolib.GeoReplacedEntityRendererMixin", "integration.geckolib.MolangQueriesMixin", "integration.geckolib.ParticleKeyframeDataMixin"