- */
-public final class ReiParticlesAPI {
- public static final ReiParticlesAPI INSTANCE = new ReiParticlesAPI();
- /** @deprecated Use {@link #reiScheduler()} for new code. This scheduler only supports one-shot server tasks. */
- @Deprecated
- public static final Scheduler scheduler = new Scheduler();
- private static final Logger LOGGER = LogUtils.getLogger();
- private static boolean initialized;
- private static boolean scannersLoaded;
- private static boolean testHooksRegistered;
-
- private ReiParticlesAPI() {
- }
-
- /** Initializes the API. Safe to call multiple times; only the first call takes effect. */
- public static void init() {
- if (initialized) {
- LOGGER.debug("init() called again — already initialized, skipping");
- return;
- }
- initialized = true;
- ParticleEmittersManager.registerBuiltinCodecs();
- LOGGER.info("ReiParticlesAPI init completed");
- }
-
- /** Returns {@code true} if {@link #init()} has been called. */
- public static boolean isInitialized() {
- return initialized;
- }
-
- /** Scans and initializes event listener packages. Call once after all listener targets are registered. */
- public void loadScannerPackages() {
- if (scannersLoaded) {
- LOGGER.debug("loadScannerPackages() called again — already loaded, skipping");
- return;
- }
- scannersLoaded = true;
- if (!initialized) {
- LOGGER.warn("loadScannerPackages() called before init() — call init() first");
- }
- LOGGER.info("ReiParticlesAPI scanner packages loaded");
- ReiEventBus.INSTANCE.initListeners();
- }
-
- public boolean scannersLoaded() {
- return scannersLoaded;
- }
-
- public void registerTest() {
- if (testHooksRegistered) return;
- testHooksRegistered = true;
- TestManager.INSTANCE.register("api-test-group-builder", user -> buildSmokeTestGroup(user));
- LOGGER.info("ReiParticlesAPI test hooks registered");
- }
-
- public boolean testHooksRegistered() {
- return testHooksRegistered;
- }
-
- /**
- * Registers a package for {@link com.reiasu.reiparticlesapi.annotations.events.EventListener @EventListener}
- * class scanning. The package is scanned via ClassGraph when {@link #initEventListeners()} is called.
- *
- * @param modId the mod identifier
- * @param packageName fully-qualified package name to scan (e.g. {@code "com.example.mymod.listeners"})
- */
- public void appendEventListenerTarget(String modId, String packageName) {
- ReiEventBus.INSTANCE.appendListenerTarget(modId, packageName);
- }
-
- /** Initializes all registered event listeners. Call after all targets have been appended. */
- public void initEventListeners() {
- if (!scannersLoaded) {
- LOGGER.debug("initEventListeners() called before loadScannerPackages() — Forge scanning is a no-op, listeners must be registered explicitly");
- }
- ReiEventBus.INSTANCE.initListeners();
- }
-
- /**
- * Manually registers a single event listener instance.
- *
- * @param modId the mod identifier
- * @param listener the listener object (must have methods annotated with event handler annotations)
- */
- public void registerEventListener(String modId, Object listener) {
- ReiEventBus.INSTANCE.registerListenerInstance(modId, listener);
- }
-
- /**
- * Fires an event through the {@link com.reiasu.reiparticlesapi.event.ReiEventBus ReiEventBus}.
- *
- * @param event the event to dispatch
- * @param event type
- * @return the same event instance (may have been modified by listeners)
- */
- public T callEvent(T event) {
- return ReiEventBus.call(event);
- }
-
- /**
- * Returns the primary scheduler. Supports one-shot, repeating, max-tick,
- * cancel predicates, and finish callbacks. Ticked on both server and client.
- *
- * @see com.reiasu.reiparticlesapi.scheduler.ReiScheduler
- */
- public static ReiScheduler reiScheduler() {
- return ReiScheduler.INSTANCE;
- }
-
- /**
- * Legacy scheduler stub that delegates all calls to {@link ReiScheduler}.
- *
- * @deprecated Use {@link #reiScheduler()} instead. This class is a thin delegate
- * and will be removed in a future version.
- */
- @Deprecated
- public static final class Scheduler {
-
- /** Delegates to {@link ReiScheduler#runTask(int, Runnable)}. */
- public void runTask(int ticks, Runnable task) {
- ReiScheduler.INSTANCE.runTask(Math.max(1, ticks), task);
- }
-
- /** No-op — {@link ReiScheduler} is ticked by the API tick handler. */
- public void tick() {
- }
-
- /** No-op — lifecycle managed by {@link ReiScheduler}. */
- public void shutdown() {
- }
- }
-
- private static SimpleTestGroupBuilder buildSmokeTestGroup(ServerPlayer user) {
- return new SimpleTestGroupBuilder("api-test-group-builder", user);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesAPIForge.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesAPIForge.java
deleted file mode 100644
index 473aa55..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesAPIForge.java
+++ /dev/null
@@ -1,183 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi;
-
-import com.reiasu.reiparticlesapi.animation.AnimateManager;
-import com.reiasu.reiparticlesapi.client.ClientTickEventForwarder;
-import com.reiasu.reiparticlesapi.commands.APICommand;
-import com.reiasu.reiparticlesapi.config.APIConfig;
-import com.reiasu.reiparticlesapi.display.DisplayEntityManager;
-import com.reiasu.reiparticlesapi.event.ForgeEventForwarder;
-import com.reiasu.reiparticlesapi.event.ReiEventBus;
-import com.reiasu.reiparticlesapi.event.events.server.ServerPostTickEvent;
-import com.reiasu.reiparticlesapi.event.events.server.ServerPreTickEvent;
-import com.reiasu.reiparticlesapi.network.ReiParticlesNetwork;
-import com.reiasu.reiparticlesapi.network.animation.PathMotionManager;
-import com.reiasu.reiparticlesapi.network.particle.composition.manager.ParticleCompositionManager;
-import com.reiasu.reiparticlesapi.network.particle.emitters.ParticleEmittersManager;
-import com.reiasu.reiparticlesapi.network.particle.emitters.environment.wind.WindDirections;
-import com.reiasu.reiparticlesapi.network.particle.emitters.type.EmittersShootTypes;
-import com.reiasu.reiparticlesapi.network.particle.style.ParticleStyleManager;
-import com.reiasu.reiparticlesapi.particles.ControllableParticleEffectManager;
-import com.reiasu.reiparticlesapi.particles.ReiModParticles;
-import com.reiasu.reiparticlesapi.particles.control.group.ClientParticleGroupManager;
-import com.reiasu.reiparticlesapi.particles.impl.particles.*;
-import com.reiasu.reiparticlesapi.renderer.client.ClientRenderEntityManager;
-import com.reiasu.reiparticlesapi.renderer.server.ServerRenderEntityManager;
-import com.reiasu.reiparticlesapi.scheduler.ReiScheduler;
-import com.reiasu.reiparticlesapi.test.TestManager;
-import com.reiasu.reiparticlesapi.utils.ClientCameraUtil;
-import com.mojang.logging.LogUtils;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.client.event.RegisterParticleProvidersEvent;
-import net.minecraftforge.common.MinecraftForge;
-import net.minecraftforge.event.RegisterCommandsEvent;
-import net.minecraftforge.fml.DistExecutor;
-import net.minecraftforge.event.TickEvent;
-import net.minecraftforge.fml.ModLoadingContext;
-import net.minecraftforge.fml.common.Mod;
-import net.minecraftforge.fml.config.ModConfig;
-import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
-import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
-import org.slf4j.Logger;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-@Mod(ReiParticlesAPIForge.MOD_ID)
-public final class ReiParticlesAPIForge {
- public static final String MOD_ID = "reiparticlesapi";
- private static final Logger LOGGER = LogUtils.getLogger();
- public ReiParticlesAPIForge() {
- registerConfig();
- registerTickCallbacks();
- registerCommands();
- initSystems();
-
- LOGGER.info("ReiParticlesAPI Forge runtime initialized");
- }
-
- // ---- Lifecycle phases ----
-
- private void registerConfig() {
- ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, APIConfig.SPEC);
- }
-
- private void registerTickCallbacks() {
- var modBus = FMLJavaModLoadingContext.get().getModEventBus();
- modBus.addListener((FMLClientSetupEvent event) -> onClientSetup());
- ReiModParticles.register(modBus);
- modBus.addListener(this::onRegisterParticleProviders);
-
- MinecraftForge.EVENT_BUS.addListener((TickEvent.ClientTickEvent event) -> {
- if (event.phase == TickEvent.Phase.START) {
- DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ClientTickEventForwarder::onClientStartTick);
- }
- });
- MinecraftForge.EVENT_BUS.addListener((TickEvent.ClientTickEvent event) -> {
- if (event.phase == TickEvent.Phase.END) {
- onClientEndTick();
- }
- });
- MinecraftForge.EVENT_BUS.addListener((TickEvent.ServerTickEvent event) -> {
- if (event.phase == TickEvent.Phase.START && event.getServer() != null) {
- ReiEventBus.call(new ServerPreTickEvent(event.getServer()));
- }
- });
- MinecraftForge.EVENT_BUS.addListener((TickEvent.ServerTickEvent event) -> {
- if (event.phase == TickEvent.Phase.END && event.getServer() != null) {
- onServerEndTick(event.getServer());
- }
- });
- }
-
- private void registerCommands() {
- MinecraftForge.EVENT_BUS.addListener(
- (RegisterCommandsEvent event) ->
- APICommand.INSTANCE.register(event.getDispatcher())
- );
- }
-
- private void initSystems() {
- ReiParticlesNetwork.init();
- ParticleEmittersManager.registerBuiltinCodecs();
- EmittersShootTypes.INSTANCE.init();
- WindDirections.INSTANCE.init();
- ControllableParticleEffectManager.INSTANCE.init();
- ReiParticlesAPI.init();
- ForgeEventForwarder.init();
- ReiParticlesAPI.INSTANCE.loadScannerPackages();
- ReiParticlesAPI.INSTANCE.registerTest();
- }
-
- // ---- Tick handlers (each manager wrapped in try-catch) ----
-
- private void onClientEndTick() {
- safeTick("ClientTickEventForwarder", () ->
- DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ClientTickEventForwarder::onClientEndTick));
- safeTick("AnimateManager.client", () -> AnimateManager.INSTANCE.tickClient());
- safeTick("ParticleEmittersManager.client", ParticleEmittersManager::tickClient);
- safeTick("DisplayEntityManager.client", () -> DisplayEntityManager.INSTANCE.tickClient());
- safeTick("ParticleCompositionManager.client", () -> ParticleCompositionManager.INSTANCE.tickClient());
- safeTick("ParticleStyleManager.client", ParticleStyleManager::doTickClient);
- safeTick("ClientParticleGroupManager", () -> ClientParticleGroupManager.INSTANCE.doClientTick());
- safeTick("ClientRenderEntityManager", () -> ClientRenderEntityManager.INSTANCE.doClientTick());
- safeTick("PathMotionManager", () -> PathMotionManager.INSTANCE.tick());
- safeTick("ReiScheduler", () -> ReiScheduler.INSTANCE.doTick());
- safeTick("ClientCameraUtil", () -> ClientCameraUtil.INSTANCE.tick());
- }
-
- private void onServerEndTick(net.minecraft.server.MinecraftServer server) {
- safeTick("AnimateManager.server", () -> AnimateManager.INSTANCE.tickServer());
- safeTick("ParticleEmittersManager.server", ParticleEmittersManager::tickAll);
- safeTick("DisplayEntityManager.server", () -> DisplayEntityManager.INSTANCE.tickAll());
- safeTick("ParticleCompositionManager.server", () -> ParticleCompositionManager.INSTANCE.tickAll());
- safeTick("ParticleStyleManager.server", ParticleStyleManager::doTickServer);
- safeTick("ServerRenderEntityManager.tick", () -> ServerRenderEntityManager.INSTANCE.tick());
- safeTick("ServerRenderEntityManager.upgrade", () -> ServerRenderEntityManager.INSTANCE.upgrade(server));
- safeTick("TestManager", () -> TestManager.INSTANCE.doTickServer());
- safeTick("ReiScheduler.server", () -> ReiScheduler.INSTANCE.doTick());
- safeTick("ServerPostTickEvent", () -> ReiEventBus.call(new ServerPostTickEvent(server)));
- }
-
- private static final Map LAST_ERROR_LOG = new ConcurrentHashMap<>();
- private static final long ERROR_LOG_INTERVAL_MS = 10_000L;
-
- private static void safeTick(String name, Runnable tick) {
- try {
- tick.run();
- } catch (Exception e) {
- long now = System.currentTimeMillis();
- Long last = LAST_ERROR_LOG.get(name);
- if (last == null || now - last >= ERROR_LOG_INTERVAL_MS) {
- LAST_ERROR_LOG.put(name, now);
- LOGGER.warn("Tick handler '{}' threw (throttled to once per {}s):",
- name, ERROR_LOG_INTERVAL_MS / 1000, e);
- }
- }
- }
-
- // ---- Setup callbacks ----
-
- private void onClientSetup() {
- LOGGER.info("ReiParticlesAPI client setup completed");
- }
-
- private void onRegisterParticleProviders(RegisterParticleProvidersEvent event) {
- event.registerSpriteSet(ReiModParticles.CONTROLLABLE_END_ROD.get(),
- ControllableEndRodParticle.Factory::new);
- event.registerSpriteSet(ReiModParticles.CONTROLLABLE_ENCHANTMENT.get(),
- ControllableEnchantmentParticle.Factory::new);
- event.registerSpriteSet(ReiModParticles.CONTROLLABLE_CLOUD.get(),
- ControllableCloudParticle.Factory::new);
- event.registerSpriteSet(ReiModParticles.CONTROLLABLE_FLASH.get(),
- ControllableFlashParticle.Factory::new);
- event.registerSpriteSet(ReiModParticles.CONTROLLABLE_FIREWORK.get(),
- ControllableFireworkParticle.Factory::new);
- event.registerSpecial(ReiModParticles.CONTROLLABLE_FALLING_DUST.get(),
- new ControllableFallingDustParticle.Factory());
- event.registerSpriteSet(ReiModParticles.CONTROLLABLE_SPLASH.get(),
- ControllableSplashParticle.Factory::new);
- LOGGER.info("Registered ReiParticlesAPI particle providers (7 types)");
- }
-}
\ No newline at end of file
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Ease.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Ease.java
deleted file mode 100644
index 6fb4fe2..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Ease.java
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.animation.timeline;
-
-/**
- * Easing function interface for timeline animations.
- * Takes a progress value t in [0,1] and returns the eased value.
- */
-@FunctionalInterface
-public interface Ease {
- double cal(double t);
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/CodecField.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/CodecField.java
deleted file mode 100644
index 0a8eb9d..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/CodecField.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Marks a field for inclusion in automatic codec serialization/deserialization.
- *
- * Fields are serialized in ascending {@link #index()} order. Two fields with the
- * same index fall back to declaration order (which is JVM-dependent and fragile).
- * Always assign explicit indices when wire compatibility matters.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.FIELD)
-public @interface CodecField {
- /**
- * Explicit serialization order index. Lower values are encoded first.
- * Default {@code 0} — fields sharing the same index are sorted by name as a fallback.
- */
- int index() default 0;
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/BufferCodec.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/BufferCodec.java
deleted file mode 100644
index 0cb9276..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/BufferCodec.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.annotations.codec;
-
-import net.minecraft.network.FriendlyByteBuf;
-
-/**
- * Replacement for Fabric/Mojang's {@code StreamCodec} which
- * was introduced in Minecraft 1.20.5 and does not exist in Forge 1.20.1.
- *
- * Provides symmetric encode/decode for a type {@code T} to/from a
- * {@link FriendlyByteBuf}.
- *
- * @param the type to serialize
- */
-public interface BufferCodec {
-
- /**
- * Creates a {@link BufferCodec} from explicit encoder and decoder lambdas.
- */
- static BufferCodec of(Encoder encoder, Decoder decoder) {
- return new BufferCodec() {
- @Override
- public void encode(FriendlyByteBuf buf, T value) {
- encoder.encode(buf, value);
- }
-
- @Override
- public T decode(FriendlyByteBuf buf) {
- return decoder.decode(buf);
- }
- };
- }
-
- /**
- * Writes {@code value} into the buffer.
- */
- void encode(FriendlyByteBuf buf, T value);
-
- /**
- * Reads a value from the buffer.
- */
- T decode(FriendlyByteBuf buf);
-
- @FunctionalInterface
- interface Encoder {
- void encode(FriendlyByteBuf buf, T value);
- }
-
- @FunctionalInterface
- interface Decoder {
- T decode(FriendlyByteBuf buf);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/barrages/Barrage.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/barrages/Barrage.java
deleted file mode 100644
index 32c0210..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/barrages/Barrage.java
+++ /dev/null
@@ -1,103 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.barrages;
-
-import com.reiasu.reiparticlesapi.network.particle.ServerController;
-import net.minecraft.world.entity.LivingEntity;
-import net.minecraft.world.phys.Vec3;
-import net.minecraft.server.level.ServerLevel;
-
-import javax.annotation.Nullable;
-import java.util.UUID;
-
-/**
- * A projectile-like entity that travels in a direction, detects hits against
- * blocks, entities, and other barrages, and can pass through targets.
- *
- * Implementations should use {@link AbstractBarrage} as a base class.
- */
-public interface Barrage {
-
- /**
- * Current world position.
- */
- Vec3 getLoc();
-
- void setLoc(Vec3 loc);
-
- /**
- * The server world this barrage exists in.
- */
- ServerLevel getWorld();
-
- /**
- * The axis-aligned hit box for collision detection.
- */
- HitBox getHitBox();
-
- void setHitBox(HitBox hitBox);
-
- /**
- * The entity that shot this barrage (may be null).
- */
- @Nullable
- LivingEntity getShooter();
-
- void setShooter(@Nullable LivingEntity shooter);
-
- /**
- * The movement direction vector.
- */
- Vec3 getDirection();
-
- void setDirection(Vec3 direction);
-
- /**
- * Whether this barrage has been launched (is actively ticking/moving).
- */
- boolean getLaunch();
-
- void setLaunch(boolean launch);
-
- /**
- * Whether this barrage is still valid (not yet removed).
- */
- boolean getValid();
-
- /**
- * Barrage configuration options.
- */
- BarrageOption getOptions();
-
- /**
- * Unique identifier for this barrage instance.
- */
- UUID getUuid();
-
- /**
- * The server-side particle/display controller bound to this barrage.
- */
- ServerController> getBindControl();
-
- /**
- * Called when a hit is detected. Implementations should invoke
- * {@link #onHit(BarrageHitResult)} then handle removal/pass-through logic.
- */
- void hit(BarrageHitResult result);
-
- /**
- * User-overridable callback when a hit occurs.
- */
- void onHit(BarrageHitResult result);
-
- /**
- * Whether this barrage currently ignores collisions
- * (e.g. during initial no-hitbox grace period).
- */
- boolean noclip();
-
- /**
- * Called each server tick to update position, detect hits, etc.
- */
- void tick();
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/display/ReiRenderTypesProvider.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/display/ReiRenderTypesProvider.java
deleted file mode 100644
index a91fc63..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/display/ReiRenderTypesProvider.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.display;
-
-import net.minecraft.client.renderer.RenderType;
-
-/**
- * Provider interface for custom glow render types used by display entities.
- */
-public interface ReiRenderTypesProvider {
- /**
- * Return the glow {@link RenderType} for rendering display entities with bloom/glow effects.
- *
- * @return a RenderType configured for glow rendering
- */
- RenderType glow();
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/enums/DistType.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/enums/DistType.java
deleted file mode 100644
index 56498fa..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/enums/DistType.java
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.enums;
-
-/**
- * Distribution side indicator.
- */
-public enum DistType {
- CLIENT,
- SERVER
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/event/api/ReiEvent.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/event/api/ReiEvent.java
deleted file mode 100644
index 54eed78..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/event/api/ReiEvent.java
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.event.api;
-
-public abstract class ReiEvent {
-}
-
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/extend/BlockPosExtendsKt.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/extend/BlockPosExtendsKt.java
deleted file mode 100644
index 115fe08..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/extend/BlockPosExtendsKt.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.extend;
-
-import net.minecraft.core.BlockPos;
-import net.minecraft.world.phys.Vec3;
-
-/**
- * Extension utility for {@link BlockPos} providing conversion from {@link Vec3}.
- * Originally a Kotlin extension function, ported as a static utility method.
- */
-public final class BlockPosExtendsKt {
-
- private BlockPosExtendsKt() {
- }
-
- /**
- * Returns the {@link BlockPos} containing the given {@link Vec3} position
- * (equivalent to flooring each component).
- */
- public static BlockPos ofFloored(Vec3 vec) {
- return BlockPos.containing(vec.x, vec.y, vec.z);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/extend/ItemStackExtendKt.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/extend/ItemStackExtendKt.java
deleted file mode 100644
index 4d9361d..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/extend/ItemStackExtendKt.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.extend;
-
-import net.minecraft.world.item.Item;
-import net.minecraft.world.item.ItemStack;
-
-/**
- * Extension utilities for {@link ItemStack}.
- * Originally Kotlin extension functions, ported as static utility methods.
- */
-public final class ItemStackExtendKt {
-
- private ItemStackExtendKt() {
- }
-
- /**
- * Checks whether this stack's item matches the given {@link Item}.
- */
- public static boolean isOf(ItemStack stack, Item item) {
- return stack.is(item);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/extend/Vec3iExtendsKt.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/extend/Vec3iExtendsKt.java
deleted file mode 100644
index 076aad5..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/extend/Vec3iExtendsKt.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.extend;
-
-import net.minecraft.core.Vec3i;
-import net.minecraft.world.phys.Vec3;
-
-/**
- * Extension utility for {@link Vec3i} providing conversion to {@link Vec3}.
- * Originally a Kotlin extension function, ported as a static utility method.
- */
-public final class Vec3iExtendsKt {
-
- private Vec3iExtendsKt() {
- }
-
- /**
- * Converts a {@link Vec3i} to a {@link Vec3} (double-precision).
- */
- public static Vec3 asVec3(Vec3i v) {
- return new Vec3(v.getX(), v.getY(), v.getZ());
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/ReiParticlesNetwork.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/ReiParticlesNetwork.java
deleted file mode 100644
index db8adff..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/ReiParticlesNetwork.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network;
-
-import com.reiasu.reiparticlesapi.ReiParticlesAPIForge;
-import com.reiasu.reiparticlesapi.network.packet.CameraShakeS2CPacket;
-import com.reiasu.reiparticlesapi.network.packet.PacketCameraShakeS2C;
-import com.reiasu.reiparticlesapi.network.packet.PacketDisplayEntityS2C;
-import com.reiasu.reiparticlesapi.network.packet.PacketKeyActionC2S;
-import com.reiasu.reiparticlesapi.network.packet.PacketParticleCompositionS2C;
-import com.reiasu.reiparticlesapi.network.packet.PacketParticleEmittersS2C;
-import com.reiasu.reiparticlesapi.network.packet.PacketParticleGroupS2C;
-import com.reiasu.reiparticlesapi.network.packet.PacketParticleS2C;
-import com.reiasu.reiparticlesapi.network.packet.PacketParticleStyleS2C;
-import com.reiasu.reiparticlesapi.network.packet.PacketRenderEntityS2C;
-import com.mojang.logging.LogUtils;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.server.level.ServerPlayer;
-import net.minecraftforge.network.NetworkRegistry;
-import net.minecraftforge.network.PacketDistributor;
-import net.minecraftforge.network.simple.SimpleChannel;
-import org.slf4j.Logger;
-
-public final class ReiParticlesNetwork {
- private static final Logger LOGGER = LogUtils.getLogger();
- private static final int PROTOCOL_VERSION = 1;
- private static int packetId = 0;
-
- public static final SimpleChannel CHANNEL = NetworkRegistry.ChannelBuilder
- .named(new ResourceLocation(ReiParticlesAPIForge.MOD_ID, "main"))
- .networkProtocolVersion(() -> Integer.toString(PROTOCOL_VERSION))
- .clientAcceptedVersions(v -> true)
- .serverAcceptedVersions(v -> true)
- .simpleChannel();
-
- private ReiParticlesNetwork() {
- }
-
- public static void init() {
- CHANNEL.registerMessage(packetId++, CameraShakeS2CPacket.class, CameraShakeS2CPacket::encode, CameraShakeS2CPacket::decode, CameraShakeS2CPacket::handle);
- CHANNEL.registerMessage(packetId++, PacketCameraShakeS2C.class, PacketCameraShakeS2C::encode, PacketCameraShakeS2C::decode, PacketCameraShakeS2C::handle);
- CHANNEL.registerMessage(packetId++, PacketParticleS2C.class, PacketParticleS2C::encode, PacketParticleS2C::decode, PacketParticleS2C::handle);
- CHANNEL.registerMessage(packetId++, PacketParticleEmittersS2C.class, PacketParticleEmittersS2C::encode, PacketParticleEmittersS2C::decode, PacketParticleEmittersS2C::handle);
- CHANNEL.registerMessage(packetId++, PacketParticleCompositionS2C.class, PacketParticleCompositionS2C::encode, PacketParticleCompositionS2C::decode, PacketParticleCompositionS2C::handle);
- CHANNEL.registerMessage(packetId++, PacketDisplayEntityS2C.class, PacketDisplayEntityS2C::encode, PacketDisplayEntityS2C::decode, PacketDisplayEntityS2C::handle);
- CHANNEL.registerMessage(packetId++, PacketParticleStyleS2C.class, PacketParticleStyleS2C::encode, PacketParticleStyleS2C::decode, PacketParticleStyleS2C::handle);
- CHANNEL.registerMessage(packetId++, PacketParticleGroupS2C.class, PacketParticleGroupS2C::encode, PacketParticleGroupS2C::decode, PacketParticleGroupS2C::handle);
- CHANNEL.registerMessage(packetId++, PacketRenderEntityS2C.class, PacketRenderEntityS2C::encode, PacketRenderEntityS2C::decode, PacketRenderEntityS2C::handle);
- CHANNEL.registerMessage(packetId++, PacketKeyActionC2S.class, PacketKeyActionC2S::encode, PacketKeyActionC2S::decode, PacketKeyActionC2S::handle);
- }
-
- public static void sendTo(ServerPlayer player, Object packet) {
- try {
- CHANNEL.send(PacketDistributor.PLAYER.with(() -> player), packet);
- } catch (IllegalArgumentException e) {
- LOGGER.debug("Player {} lacks channel — packet {} dropped",
- player.getName().getString(), packet.getClass().getSimpleName());
- } catch (RuntimeException e) {
- LOGGER.debug("Failed to send packet {} to {}: {}", packet.getClass().getSimpleName(),
- player.getName().getString(), e.getMessage());
- }
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/animation/LambdaParticlePathMotion.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/animation/LambdaParticlePathMotion.java
deleted file mode 100644
index 45f8121..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/animation/LambdaParticlePathMotion.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.animation;
-
-import com.reiasu.reiparticlesapi.particles.ControllableParticle;
-import net.minecraft.world.phys.Vec3;
-
-import java.util.function.IntFunction;
-
-/**
- * Concrete path motion that uses a lambda to compute the path offset.
- * The function takes the current tick index and returns a Vec3 offset from origin.
- *
- * Inherits teleport and validity logic from {@link ParticlePathMotion}.
- */
-public final class LambdaParticlePathMotion extends ParticlePathMotion {
- private final IntFunction path;
-
- /**
- * @param origin the world origin position
- * @param particle the target particle
- * @param path function from tick index to Vec3 offset
- */
- public LambdaParticlePathMotion(Vec3 origin, ControllableParticle particle, IntFunction path) {
- super(origin, particle);
- this.path = path;
- }
-
- public IntFunction getPath() {
- return path;
- }
-
- @Override
- public Vec3 pathFunction() {
- return path.apply(getCurrentTick());
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/animation/api/PathMotion.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/animation/api/PathMotion.java
deleted file mode 100644
index e64e533..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/animation/api/PathMotion.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.animation.api;
-
-import net.minecraft.world.phys.Vec3;
-
-/**
- * Interface defining path-based motion for particle entities.
- * Implementations compute per-tick offsets from an origin, and apply
- * the resulting world position to their target.
- */
-public interface PathMotion {
- int getCurrentTick();
-
- void setCurrentTick(int tick);
-
- Vec3 getOrigin();
-
- void setOrigin(Vec3 origin);
-
- /**
- * Apply the computed world position to the motion target
- * (e.g. teleport a style/emitter to this position).
- */
- void apply(Vec3 actualPos);
-
- /**
- * Compute and return the next path offset from origin.
- * This also advances the internal tick counter.
- */
- Vec3 next();
-
- /**
- * Check whether the motion target is still alive/valid.
- * Returns false if the target has been removed or cancelled.
- */
- boolean checkValid();
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/packet/PacketDisplayEntityS2C.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/packet/PacketDisplayEntityS2C.java
deleted file mode 100644
index 7cce080..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/packet/PacketDisplayEntityS2C.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.packet;
-
-import com.reiasu.reiparticlesapi.network.packet.client.listener.ClientDisplayEntityPacketHandler;
-import net.minecraft.network.FriendlyByteBuf;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.fml.DistExecutor;
-import net.minecraftforge.network.NetworkEvent;
-
-import java.util.UUID;
-import java.util.function.Supplier;
-
-public record PacketDisplayEntityS2C(UUID uuid, String type, byte[] data) {
- public static void encode(PacketDisplayEntityS2C packet, FriendlyByteBuf buf) {
- buf.writeUtf(packet.type);
- buf.writeUUID(packet.uuid);
- buf.writeInt(packet.data.length);
- buf.writeBytes(packet.data);
- }
-
- public static PacketDisplayEntityS2C decode(FriendlyByteBuf buf) {
- String type = buf.readUtf();
- UUID uuid = buf.readUUID();
- int size = buf.readInt();
- byte[] data = new byte[size];
- buf.readBytes(data);
- return new PacketDisplayEntityS2C(uuid, type, data);
- }
-
- public static void handle(PacketDisplayEntityS2C packet, Supplier contextSupplier) {
- NetworkEvent.Context context = contextSupplier.get();
- context.enqueueWork(() -> DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientDisplayEntityPacketHandler.receive(packet)));
- context.setPacketHandled(true);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/packet/PacketParticleS2C.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/packet/PacketParticleS2C.java
deleted file mode 100644
index 91a551e..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/packet/PacketParticleS2C.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.packet;
-
-import com.reiasu.reiparticlesapi.network.packet.client.listener.ClientParticlePacketHandler;
-import net.minecraft.core.particles.ParticleOptions;
-import net.minecraft.core.particles.ParticleType;
-import net.minecraft.core.particles.ParticleTypes;
-import net.minecraft.core.registries.BuiltInRegistries;
-import net.minecraft.network.FriendlyByteBuf;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.phys.Vec3;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.fml.DistExecutor;
-import net.minecraftforge.network.NetworkEvent;
-
-import java.util.function.Supplier;
-
-public record PacketParticleS2C(ParticleOptions type, Vec3 pos, Vec3 velocity) {
- public static void encode(PacketParticleS2C packet, FriendlyByteBuf buf) {
- ResourceLocation id = BuiltInRegistries.PARTICLE_TYPE.getKey(packet.type.getType());
- buf.writeResourceLocation(id);
- packet.type.writeToNetwork(buf);
- buf.writeDouble(packet.pos.x);
- buf.writeDouble(packet.pos.y);
- buf.writeDouble(packet.pos.z);
- buf.writeDouble(packet.velocity.x);
- buf.writeDouble(packet.velocity.y);
- buf.writeDouble(packet.velocity.z);
- }
-
- public static PacketParticleS2C decode(FriendlyByteBuf buf) {
- ResourceLocation id = buf.readResourceLocation();
- ParticleType> particleType = BuiltInRegistries.PARTICLE_TYPE.get(id);
- if (particleType == null) {
- particleType = ParticleTypes.END_ROD;
- }
- ParticleOptions options = readParticleOptions(particleType, buf);
- Vec3 pos = new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble());
- Vec3 velocity = new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble());
- return new PacketParticleS2C(options, pos, velocity);
- }
-
- public static void handle(PacketParticleS2C packet, Supplier contextSupplier) {
- NetworkEvent.Context context = contextSupplier.get();
- context.enqueueWork(() -> DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientParticlePacketHandler.receive(packet)));
- context.setPacketHandled(true);
- }
-
- @SuppressWarnings({"rawtypes", "unchecked"})
- private static ParticleOptions readParticleOptions(ParticleType> type, FriendlyByteBuf buf) {
- return ((ParticleOptions.Deserializer) type.getDeserializer()).fromNetwork((ParticleType) type, buf);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/composition/AutoSequencedParticleComposition.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/composition/AutoSequencedParticleComposition.java
deleted file mode 100644
index ddfb3fd..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/composition/AutoSequencedParticleComposition.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.composition;
-
-import com.reiasu.reiparticlesapi.annotations.codec.BufferCodec;
-import com.reiasu.reiparticlesapi.annotations.composition.handler.ParticleCompositionHelper;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.phys.Vec3;
-
-/**
- * Abstract sequenced composition that auto-generates its network codec
- * via {@link ParticleCompositionHelper}.
- *
- * Subclasses must:
- *
- *
Provide a public constructor {@code (Vec3, Level)}
- *
Annotate serializable fields with {@link com.reiasu.reiparticlesapi.annotations.CodecField}
- *
Implement {@link #getParticleSequenced()} and {@link #onDisplay()}
- *
- */
-public abstract class AutoSequencedParticleComposition extends SequencedParticleComposition {
-
- protected AutoSequencedParticleComposition(Vec3 position, Level world) {
- super(position, world);
- }
-
- protected AutoSequencedParticleComposition(Vec3 position) {
- super(position);
- }
-
- /**
- * Returns the auto-generated codec for this composition type.
- * Uses reflection to serialize all {@link com.reiasu.reiparticlesapi.annotations.CodecField}
- * annotated fields.
- */
- public BufferCodec getCodec() {
- return ParticleCompositionHelper.INSTANCE.generateCodec(this);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/data/IntRangeData.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/data/IntRangeData.java
deleted file mode 100644
index 542b20b..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/data/IntRangeData.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.data;
-
-import java.util.concurrent.ThreadLocalRandom;
-
-/**
- * A range of integer values with random sampling support.
- *
- * The {@link #random()} method returns a value in {@code [min, max)} following
- * the original Kotlin Random.nextInt(min, max) semantics.
- */
-public final class IntRangeData extends RangeData {
-
- public IntRangeData(int min, int max) {
- super(min, max);
- }
-
- /**
- * Returns a random integer in {@code [min, max)}.
- */
- public int random() {
- return ThreadLocalRandom.current().nextInt(getMin(), getMax());
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/data/IntRangeDataKt.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/data/IntRangeDataKt.java
deleted file mode 100644
index 8f61d96..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/data/IntRangeDataKt.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.data;
-
-/**
- * Utility methods for {@link IntRangeData}, ported from Kotlin extension functions.
- */
-public final class IntRangeDataKt {
-
- private IntRangeDataKt() {
- }
-
- /**
- * Checks whether the given integer falls within the range (inclusive on both ends).
- */
- public static boolean isIn(int value, IntRangeData range) {
- return value >= range.getMin() && value <= range.getMax();
- }
-
- /**
- * Creates an {@link IntRangeData} where the receiver is the min.
- */
- public static IntRangeData minRangeTo(int min, int max) {
- return new IntRangeData(min, max);
- }
-
- /**
- * Creates an {@link IntRangeData} where the receiver is the max.
- */
- public static IntRangeData maxRangeTo(int max, int min) {
- return new IntRangeData(min, max);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/data/SerializableData.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/data/SerializableData.java
deleted file mode 100644
index 6393038..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/data/SerializableData.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.data;
-
-import com.reiasu.reiparticlesapi.particles.ParticleDisplayer;
-
-/**
- * Marker interface for data objects that can be network-serialized and
- * used to create a {@link ParticleDisplayer} on the client side.
- *
- * Forge port note: the original Fabric {@code getCodec()} returned a
- * StreamCodec for network serialization. In the Forge port, serialization
- * is handled at a higher level using FriendlyByteBuf directly, so the
- * codec method is omitted. Implementations should still support
- * clone and createDisplayer.
- */
-public interface SerializableData {
-
- /**
- * Returns a deep copy of this data.
- */
- SerializableData clone();
-
- /**
- * Creates a {@link ParticleDisplayer} that renders the particle
- * described by this data.
- */
- ParticleDisplayer createDisplayer();
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/ParticleDataFactory.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/ParticleDataFactory.java
deleted file mode 100644
index 79b20fd..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/ParticleDataFactory.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.emitters;
-
-/**
- * Factory interface for creating {@link ControllableParticleData} instances.
- *
- * Used by emitter implementations to produce the initial per-particle data
- * when spawning new particles each tick.
- */
-@FunctionalInterface
-public interface ParticleDataFactory {
-
- /**
- * Creates a new {@link ControllableParticleData} with default values.
- */
- ControllableParticleData create();
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/PhysicConstant.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/PhysicConstant.java
deleted file mode 100644
index 952fcd6..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/PhysicConstant.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.emitters;
-
-/**
- * Physics constants used by particle emitter simulations.
- * Values are tuned for Minecraft's coordinate and tick system.
- */
-public final class PhysicConstant {
- public static final PhysicConstant INSTANCE = new PhysicConstant();
-
- /** Gravity acceleration per tick (blocks/tick^2). Minecraft default ~0.05 */
- public static final double EARTH_GRAVITY = 0.05;
-
- /** Sea-level air density (kg/m^3) */
- public static final double SEA_AIR_DENSITY = 1.225;
-
- /** Drag coefficient for particle air resistance */
- public static final double DRAG_COEFFICIENT = 0.01;
-
- /** Cross-sectional area for drag calculations */
- public static final double CROSS_SECTIONAL_AREA = 0.01;
-
- private PhysicConstant() {
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/command/ParticleCommand.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/command/ParticleCommand.java
deleted file mode 100644
index 2866a7d..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/command/ParticleCommand.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.emitters.command;
-
-import com.reiasu.reiparticlesapi.network.particle.emitters.ControllableParticleData;
-import com.reiasu.reiparticlesapi.particles.ControllableParticle;
-
-/**
- * A command that mutates particle state during the emitter tick loop.
- */
-@FunctionalInterface
-public interface ParticleCommand {
-
- /**
- * Execute this command against the given particle data and particle instance.
- *
- * @param data the mutable particle data
- * @param particle the particle instance
- */
- void execute(ControllableParticleData data, ControllableParticle particle);
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/environment/wind/WindDirection.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/environment/wind/WindDirection.java
deleted file mode 100644
index 56050f9..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/environment/wind/WindDirection.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.emitters.environment.wind;
-
-import com.reiasu.reiparticlesapi.network.particle.emitters.ParticleEmitters;
-import net.minecraft.world.phys.Vec3;
-
-/**
- * Defines a wind effect that applies directional force to particles within an emitter.
- *
- * Wind can be absolute (fixed direction) or relative to the emitter position.
- * The {@code windSpeedExpress} field allows configuring wind strength; in the original
- * Fabric version this was a math expression with variable {@code l} (distance),
- * but in the Forge port it is parsed as a constant double for simplicity.
- *
- * Forge port note: {@code getCodec()} from Fabric StreamCodec is omitted.
- */
-public interface WindDirection {
-
- /**
- * The base wind direction vector.
- */
- Vec3 getDirection();
-
- void setDirection(Vec3 direction);
-
- /**
- * Whether wind is relative to the emitter position (true) or absolute (false).
- */
- boolean getRelative();
-
- void setRelative(boolean relative);
-
- /**
- * Wind speed expression string. Parsed as a constant double in Forge port.
- * In Fabric, this was a math expression with variable {@code l} = distance.
- */
- String getWindSpeedExpress();
-
- void setWindSpeedExpress(String express);
-
- /**
- * Bind this wind direction to an emitter instance for relative calculations.
- */
- WindDirection loadEmitters(ParticleEmitters emitters);
-
- /**
- * Whether an emitter has been bound.
- */
- boolean hasLoadedEmitters();
-
- /**
- * Unique string identifier for this wind type.
- */
- String getID();
-
- /**
- * Compute the wind vector at a given particle position.
- *
- * @param particlePos the particle's current world position
- * @return the wind force vector
- */
- Vec3 getWind(Vec3 particlePos);
-
- /**
- * Check whether the given position is within this wind's area of effect.
- */
- boolean inRange(Vec3 pos);
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/event/ParticleEvent.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/event/ParticleEvent.java
deleted file mode 100644
index 259e1de..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/event/ParticleEvent.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.emitters.event;
-
-import com.reiasu.reiparticlesapi.network.particle.emitters.ControllableParticleData;
-import com.reiasu.reiparticlesapi.particles.ControllableParticle;
-
-/**
- * Base interface for particle-level events fired during emitter simulation.
- */
-public interface ParticleEvent {
-
- /**
- * Returns the unique string identifier for this event type.
- */
- String getEventID();
-
- /**
- * Gets the particle instance.
- */
- ControllableParticle getParticle();
-
- /**
- * Sets the particle instance.
- */
- void setParticle(ControllableParticle particle);
-
- /**
- * Gets the particle's mutable data.
- */
- ControllableParticleData getParticleData();
-
- /**
- * Sets the particle's mutable data.
- */
- void setParticleData(ControllableParticleData data);
-
- /**
- * Whether this event has been canceled by a handler.
- */
- boolean getCanceled();
-
- /**
- * Cancel or un-cancel this event.
- */
- void setCanceled(boolean canceled);
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/event/ParticleEventHandler.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/event/ParticleEventHandler.java
deleted file mode 100644
index c857ea6..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/event/ParticleEventHandler.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.emitters.event;
-
-/**
- * A handler that reacts to a specific {@link ParticleEvent} type.
- * Handlers are ordered by priority (lower priority value = executed first).
- */
-public interface ParticleEventHandler extends Comparable {
-
- /**
- * Process the given event. May mutate event state or cancel it.
- */
- void handle(ParticleEvent event);
-
- /**
- * The event ID this handler targets (must match {@link ParticleEvent#getEventID()}).
- */
- String getTargetEventID();
-
- /**
- * Unique identifier for this handler instance.
- */
- String getHandlerID();
-
- /**
- * Execution priority. Lower values run first.
- */
- int getPriority();
-
- @Override
- default int compareTo(ParticleEventHandler other) {
- return this.getPriority() - other.getPriority();
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/event/ParticleEventHandlerManager.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/event/ParticleEventHandlerManager.java
deleted file mode 100644
index b80b6c2..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/event/ParticleEventHandlerManager.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.emitters.event;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Singleton registry for {@link ParticleEventHandler} instances.
- *
- * Forge port note: The Fabric version used {@code ReiAPIScanner} for classpath-scanning
- * auto-registration. In the Forge port, handlers must be registered explicitly via
- * {@link #register(ParticleEventHandler)}.
- */
-public final class ParticleEventHandlerManager {
-
- public static final ParticleEventHandlerManager INSTANCE = new ParticleEventHandlerManager();
-
- private final Map registerHandlers = new HashMap<>();
-
- private ParticleEventHandlerManager() {
- }
-
- /**
- * Look up a handler by its handler ID.
- */
- public ParticleEventHandler getHandlerById(String id) {
- return registerHandlers.get(id);
- }
-
- /**
- * Register a handler. Replaces any existing handler with the same ID.
- */
- public void register(ParticleEventHandler handler) {
- registerHandlers.put(handler.getHandlerID(), handler);
- }
-
- /**
- * Check whether a handler with the given ID is already registered.
- */
- public boolean hasRegister(String id) {
- return registerHandlers.containsKey(id);
- }
-
- /**
- * Called during mod init. In the Forge port this is a no-op;
- * handlers should register themselves explicitly.
- */
- public void init() {
- // Fabric version used ReiAPIScanner for auto-registration.
- // Forge port: register handlers explicitly or use Forge event bus.
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/type/EmittersShootType.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/type/EmittersShootType.java
deleted file mode 100644
index fac8214..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/network/particle/emitters/type/EmittersShootType.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.network.particle.emitters.type;
-
-import net.minecraft.world.phys.Vec3;
-
-import java.util.List;
-
-/**
- * Defines how particles are spatially distributed when an emitter fires.
- *
- * Each implementation produces a list of spawn positions relative to the emitter origin
- * and computes a default direction vector for each particle.
- *
- * Forge port note: {@code getCodec()} from Fabric StreamCodec is omitted;
- * Forge networking uses FriendlyByteBuf directly where needed.
- */
-public interface EmittersShootType {
-
- /**
- * Returns the unique string identifier for this shoot type (e.g. "point", "line").
- */
- String getID();
-
- /**
- * Computes the list of spawn positions for a batch of particles.
- *
- * @param origin the emitter world position
- * @param tick the current emitter tick
- * @param count the number of particles to produce
- * @return list of world-space spawn positions (size == count)
- */
- List getPositions(Vec3 origin, int tick, int count);
-
- /**
- * Computes the default movement direction for a particle.
- *
- * @param enter the emitter's base direction vector (may be zero)
- * @param tick the current emitter tick
- * @param pos the individual particle spawn position
- * @param origin the emitter world position
- * @return the direction vector for the particle
- */
- Vec3 getDefaultDirection(Vec3 enter, int tick, Vec3 pos, Vec3 origin);
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/Controllable.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/Controllable.java
deleted file mode 100644
index 9fea7de..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/Controllable.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.particles;
-
-import com.reiasu.reiparticlesapi.network.buffer.ParticleControllerDataBuffer;
-import com.reiasu.reiparticlesapi.utils.RelativeLocation;
-import net.minecraft.world.phys.Vec3;
-
-import java.util.Map;
-import java.util.UUID;
-
-/**
- * Base interface for objects that can be remotely controlled (particles, compositions, etc.).
- *
- * @param the type of the underlying controlled object
- */
-public interface Controllable {
-
- /**
- * The unique control UUID for this controllable.
- */
- UUID controlUUID();
-
- /**
- * Rotate this controllable to face the given point.
- */
- void rotateToPoint(RelativeLocation to);
-
- /**
- * Rotate this controllable to face the given point with an additional angle offset.
- */
- void rotateToWithAngle(RelativeLocation to, double radian);
-
- /**
- * Rotate this controllable around its axis by the given angle.
- */
- void rotateAsAxis(double radian);
-
- /**
- * Teleport this controllable to the given position.
- */
- void teleportTo(Vec3 pos);
-
- /**
- * Teleport this controllable to the given coordinates.
- */
- void teleportTo(double x, double y, double z);
-
- /**
- * Remove/destroy this controllable.
- */
- void remove();
-
- /**
- * Get the underlying controlled object.
- */
- T getControlObject();
-
- /**
- * Get the controlled object cast to type S.
- */
- @SuppressWarnings("unchecked")
- default S getControlCasted() {
- return (S) getControlObject();
- }
-
- /**
- * Get the controlled object cast to type S, or null if the cast fails.
- */
- @SuppressWarnings("unchecked")
- default S getControlCastedOrNull() {
- try {
- return (S) getControlObject();
- } catch (Exception e) {
- return null;
- }
- }
-
- // ---- Buffer-based load/change (used by composition system) ----
-
- default void load(Map> args) {
- }
-
- default Map> toArgs() {
- return Map.of();
- }
-
- default void change(Map> args) {
- load(args);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/ControllableParticleEffect.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/ControllableParticleEffect.java
deleted file mode 100644
index 5ceae1c..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/ControllableParticleEffect.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.particles;
-
-import net.minecraft.core.particles.ParticleOptions;
-
-import java.util.UUID;
-
-/**
- * Marker interface for controllable particle effects.
- *
- * Extends {@link ParticleOptions} so implementations can be used with
- * {@link net.minecraft.client.particle.ParticleProvider} factories.
- * The actual serialization is handled by the ReiParticles custom network,
- * so {@link ParticleOptions} methods are implemented with defaults in
- * each concrete class.
- */
-public interface ControllableParticleEffect extends ParticleOptions {
- UUID getControlUUID();
- void setControlUUID(UUID uuid);
- boolean getFaceToPlayer();
- ControllableParticleEffect clone();
-}
\ No newline at end of file
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/ParticleLerpInterpolator.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/ParticleLerpInterpolator.java
deleted file mode 100644
index 4d4920a..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/ParticleLerpInterpolator.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.particles;
-
-import net.minecraft.world.phys.Vec3;
-
-/**
- * Interpolator for particle position lerping between ticks.
- */
-public interface ParticleLerpInterpolator {
- /**
- * Compute an interpolated position between previous and current tick positions.
- *
- * @param prev the position at the previous tick
- * @param current the position at the current tick
- * @param delta the partial-tick fraction (0..1)
- * @return the interpolated position
- */
- Vec3 consume(Vec3 prev, Vec3 current, float delta);
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/ReiModParticles.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/ReiModParticles.java
deleted file mode 100644
index 2d6fc56..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/ReiModParticles.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.particles;
-
-import com.reiasu.reiparticlesapi.particles.impl.*;
-import com.mojang.brigadier.StringReader;
-import net.minecraft.core.particles.ParticleOptions;
-import net.minecraft.core.particles.ParticleType;
-import net.minecraft.network.FriendlyByteBuf;
-import net.minecraftforge.eventbus.api.IEventBus;
-import net.minecraftforge.registries.DeferredRegister;
-import net.minecraftforge.registries.ForgeRegistries;
-import net.minecraftforge.registries.RegistryObject;
-
-import java.util.UUID;
-import java.util.function.BiFunction;
-
-/**
- * Registry for all ReiParticlesAPI custom particle types.
- *
- * Forge port: uses {@link DeferredRegister} with custom {@link ParticleType}
- * instances that carry {@link ControllableParticleEffect} data (UUID + faceToPlayer).
- */
-public final class ReiModParticles {
- public static final ReiModParticles INSTANCE = new ReiModParticles();
-
- private static final DeferredRegister> PARTICLES =
- DeferredRegister.create(ForgeRegistries.PARTICLE_TYPES, "reiparticlesapi");
-
- public static final RegistryObject> CONTROLLABLE_END_ROD =
- PARTICLES.register("controllable_end_rod",
- () -> createType(ControllableEndRodEffect::new));
-
- public static final RegistryObject> CONTROLLABLE_ENCHANTMENT =
- PARTICLES.register("controllable_enchantment",
- () -> createType(ControllableEnchantmentEffect::new));
-
- public static final RegistryObject> CONTROLLABLE_CLOUD =
- PARTICLES.register("controllable_cloud",
- () -> createType(ControllableCloudEffect::new));
-
- public static final RegistryObject> CONTROLLABLE_FLASH =
- PARTICLES.register("controllable_flash",
- () -> createType(ControllableFlashEffect::new));
-
- public static final RegistryObject> CONTROLLABLE_FIREWORK =
- PARTICLES.register("controllable_firework",
- () -> createType(ControllableFireworkEffect::new));
-
- public static final RegistryObject> CONTROLLABLE_FALLING_DUST =
- PARTICLES.register("controllable_falling_dust",
- () -> createType((uuid, face) -> new ControllableFallingDustEffect(
- uuid, net.minecraft.world.level.block.Blocks.SAND.defaultBlockState(), face)));
-
- public static final RegistryObject> CONTROLLABLE_SPLASH =
- PARTICLES.register("controllable_splash",
- () -> createType(ControllableSplashEffect::new));
-
- private ReiModParticles() {}
-
- /**
- * Creates a {@link ParticleType} with a deserializer that reads UUID + boolean
- * from the network/command, matching all {@link ControllableParticleEffect} subtypes.
- */
- @SuppressWarnings("deprecation")
- private static ParticleType createType(
- BiFunction factory) {
- ParticleOptions.Deserializer deserializer = new ParticleOptions.Deserializer() {
- @Override
- public T fromCommand(ParticleType type, StringReader reader) {
- return factory.apply(UUID.randomUUID(), false);
- }
-
- @Override
- public T fromNetwork(ParticleType type, FriendlyByteBuf buf) {
- return factory.apply(buf.readUUID(), buf.readBoolean());
- }
- };
- return new ParticleType(true, deserializer) {
- @Override
- public com.mojang.serialization.Codec codec() {
- return com.mojang.serialization.Codec.unit(() -> factory.apply(UUID.randomUUID(), false));
- }
- };
- }
-
- /**
- * Register the particle types with the Forge event bus.
- * Call this during mod construction.
- */
- public static void register(IEventBus modBus) {
- PARTICLES.register(modBus);
- }
-
- public void reg() {
- // No-op, registration is handled by DeferredRegister
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/control/ControlParticleManager.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/control/ControlParticleManager.java
deleted file mode 100644
index 5bb6dee..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/control/ControlParticleManager.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.particles.control;
-
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Manages the mapping between control UUIDs and their {@link ParticleController} instances.
- *
- * Each controlled particle gets a UUID; the manager stores and retrieves the
- * corresponding controller so the particle can be mutated from server-driven
- * commands.
- */
-public final class ControlParticleManager {
-
- public static final ControlParticleManager INSTANCE = new ControlParticleManager();
-
- private final ConcurrentHashMap controls = new ConcurrentHashMap<>();
-
- private ControlParticleManager() {
- }
-
- /**
- * Look up a controller by UUID. Returns {@code null} if none is registered.
- */
- public ParticleController getControl(UUID uuid) {
- return controls.get(uuid);
- }
-
- /**
- * Remove a controller by UUID.
- */
- public void removeControl(UUID uuid) {
- controls.remove(uuid);
- }
-
- /**
- * Create a new controller for the given UUID and register it.
- *
- * @param uuid the control UUID
- * @return the newly created controller
- */
- public ParticleController createControl(UUID uuid) {
- ParticleController controller = new ParticleController(uuid);
- controls.put(uuid, controller);
- return controller;
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/control/group/ControllableParticleGroupProvider.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/control/group/ControllableParticleGroupProvider.java
deleted file mode 100644
index ff43293..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/particles/control/group/ControllableParticleGroupProvider.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.particles.control.group;
-
-import com.reiasu.reiparticlesapi.network.buffer.ParticleControllerDataBuffer;
-
-import java.util.Map;
-import java.util.UUID;
-
-/**
- * Provider (factory) for creating and modifying {@link ControllableParticleGroup} instances.
- *
- * Registered with {@link ClientParticleGroupManager} for client-side group creation
- * when receiving server-side packets.
- *
- * @deprecated Use ParticleGroupStyle instead.
- */
-@Deprecated
-public interface ControllableParticleGroupProvider {
-
- /**
- * Create a new particle group with the given UUID and initialization args.
- */
- ControllableParticleGroup createGroup(UUID uuid, Map> args);
-
- /**
- * Apply changes to an existing group with the given args.
- */
- void changeGroup(ControllableParticleGroup group, Map> args);
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/reflect/SimpleClassInfo.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/reflect/SimpleClassInfo.java
deleted file mode 100644
index 35923ca..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/reflect/SimpleClassInfo.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.reflect;
-
-import java.lang.annotation.Annotation;
-import java.util.HashSet;
-
-/**
- * Lightweight class metadata holder storing a class name and its annotation names.
- * Used by classpath scanning to defer class loading until needed.
- */
-public final class SimpleClassInfo {
-
- private final String type;
- private final HashSet annotations;
-
- public SimpleClassInfo(String type, HashSet annotations) {
- this.type = type;
- this.annotations = annotations;
- }
-
- public String getType() {
- return type;
- }
-
- public HashSet getAnnotations() {
- return annotations;
- }
-
- /**
- * Checks whether this class info contains the given annotation type.
- */
- public boolean isAnnotationPresent(Class extends Annotation> anno) {
- return annotations.contains(anno.getName());
- }
-
- /**
- * Loads and returns the actual {@link Class} represented by this info.
- *
- * @throws ClassNotFoundException if the class cannot be found
- */
- public Class> toClass() throws ClassNotFoundException {
- return Class.forName(type);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/GraphMathHelper.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/GraphMathHelper.java
deleted file mode 100644
index a1a37b8..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/GraphMathHelper.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.utils;
-
-import net.minecraft.world.phys.Vec3;
-
-public final class GraphMathHelper {
- private GraphMathHelper() {
- }
-
- public static float lerp(float alpha, float from, float to) {
- return from + (to - from) * alpha;
- }
-
- public static double lerp(double alpha, double from, double to) {
- return from + (to - from) * alpha;
- }
-
- /**
- * Linearly interpolate between two {@link Vec3} positions.
- *
- * @param alpha interpolation factor (0 = from, 1 = to)
- * @param from start position
- * @param to end position
- * @return interpolated position
- */
- public static Vec3 lerp(float alpha, Vec3 from, Vec3 to) {
- return new Vec3(
- lerp((double) alpha, from.x, to.x),
- lerp((double) alpha, from.y, to.y),
- lerp((double) alpha, from.z, to.z)
- );
- }
-
- public static double inverseLerp(double value, double min, double max) {
- if (max == min) {
- return 0.0;
- }
- return (value - min) / (max - min);
- }
-
- public static double smoothstep(double edge0, double edge1, double x) {
- double t = Math3DUtil.clamp(inverseLerp(x, edge0, edge1), 0.0, 1.0);
- return t * t * (3.0 - 2.0 * t);
- }
-
- /**
- * Inverse-power distance falloff. Returns a value in [0, 1] that decreases
- * with distance according to {@code 1 / (1 + (dist/range)^power)}.
- *
- * When {@code dist == 0}, returns 1.0. When {@code dist == range}, returns 0.5
- * for {@code power == 1}.
- *
- * @param dist the distance from the source
- * @param range the characteristic range (half-strength distance at power=1)
- * @param power the falloff exponent (higher = steeper)
- * @return falloff factor in [0, 1]
- */
- public static double inversePowerFalloff(double dist, double range, double power) {
- if (range <= 0.0) {
- return 0.0;
- }
- double ratio = dist / range;
- return 1.0 / (1.0 + Math.pow(ratio, power));
- }
-
- /**
- * Exponential damping factor for velocity drag.
- * Returns {@code exp(-damping * dt)}, clamped to [0, 1].
- *
- * @param damping the damping coefficient (higher = more drag)
- * @param dt the time step
- * @return multiplicative factor in [0, 1]
- */
- public static double expDampFactor(double damping, double dt) {
- double factor = Math.exp(-damping * dt);
- return Math.max(0.0, Math.min(1.0, factor));
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/ReflectUtil.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/ReflectUtil.java
deleted file mode 100644
index 6debd4d..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/ReflectUtil.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.utils;
-
-import com.mojang.logging.LogUtils;
-import org.slf4j.Logger;
-
-import java.util.function.Supplier;
-
-/**
- * Reflection and timing utilities.
- *
- * Provides class references for common Minecraft types (useful for reflection-based
- * annotation processing) and timed execution helpers that log elapsed time
- * to the mod logger.
- */
-public final class ReflectUtil {
-
- private static final Logger LOGGER = LogUtils.getLogger();
-
- private ReflectUtil() {
- }
-
- /**
- * Runs a {@link Runnable} and logs the elapsed time in milliseconds.
- *
- * @param name the operation name for logging
- * @param invoker the code to run
- */
- public static void infoTimeWith(String name, Runnable invoker) {
- long start = System.currentTimeMillis();
- invoker.run();
- long end = System.currentTimeMillis();
- LOGGER.info("Executed " + name + " completed: took " + (end - start) + "ms");
- }
-
- /**
- * Overload with default empty name.
- */
- public static void infoTimeWith(Runnable invoker) {
- infoTimeWith("", invoker);
- }
-
- /**
- * Runs a {@link Supplier} and logs the elapsed time, returning the result.
- *
- * @param name the operation name for logging
- * @param invoker the code to run
- * @param return type
- * @return the result of the supplier
- */
- public static T infoTimeCallable(String name, Supplier invoker) {
- long start = System.currentTimeMillis();
- T result = invoker.get();
- long end = System.currentTimeMillis();
- LOGGER.info("Executed and returned " + name + " completed: took " + (end - start) + "ms");
- return result;
- }
-
- /**
- * Overload with default empty name.
- */
- public static T infoTimeCallable(Supplier invoker) {
- return infoTimeCallable("", invoker);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/api/ModelPartPointCollector.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/api/ModelPartPointCollector.java
deleted file mode 100644
index a44efde..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/api/ModelPartPointCollector.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.utils.api;
-
-import com.mojang.blaze3d.vertex.PoseStack;
-import net.minecraft.client.model.geom.ModelPart;
-import net.minecraft.world.phys.Vec3;
-
-import java.util.List;
-
-/**
- * Collects sample points from a {@link ModelPart} hierarchy for particle placement.
- *
- * Implementations walk the model-part tree and emit world-space positions
- * that can be used to spawn particles along the model's surface.
- */
-public interface ModelPartPointCollector {
- /**
- * Collect sample points from the given model-part tree.
- *
- * @param root the root model part
- * @param poseStack the current pose stack (transformations applied to the model)
- * @param density how many sample points to generate (higher = denser)
- * @param pixelToUnit whether to convert pixel coordinates to unit (1/16) scale
- * @return a list of sample positions in model-local space
- */
- List collectSamplePoints(ModelPart root, PoseStack poseStack, int density, boolean pixelToUnit);
-
- /**
- * Convenience overload with {@code pixelToUnit} defaulting to {@code true}.
- */
- default List collectSamplePoints(ModelPart root, PoseStack poseStack, int density) {
- return collectSamplePoints(root, poseStack, density, true);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/helper/emitters/LinearResistanceHelper.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/helper/emitters/LinearResistanceHelper.java
deleted file mode 100644
index 0947818..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/helper/emitters/LinearResistanceHelper.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.utils.helper.emitters;
-
-import net.minecraft.world.phys.Vec3;
-
-/**
- * Utility for applying linear resistance (velocity scaling) to emitter motion.
- */
-public final class LinearResistanceHelper {
- public static final LinearResistanceHelper INSTANCE = new LinearResistanceHelper();
-
- private LinearResistanceHelper() {
- }
-
- /**
- * Scale the velocity vector by a percentage factor.
- *
- * @param enter the current velocity
- * @param percent the scaling factor (e.g. 0.95 for 5% deceleration per tick)
- * @return the scaled velocity
- */
- public Vec3 setPercentageVelocity(Vec3 enter, double percent) {
- return enter.scale(percent);
- }
-}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/interpolator/data/InterpolatorData.java b/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/interpolator/data/InterpolatorData.java
deleted file mode 100644
index 07bbcf3..0000000
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/utils/interpolator/data/InterpolatorData.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
-package com.reiasu.reiparticlesapi.utils.interpolator.data;
-
-/**
- * Generic interpolation data container.
- * Stores a current value and provides interpolated results based on a progress factor.
- *
- * @param the value type
- */
-public interface InterpolatorData {
- InterpolatorData update(T current);
-
- T getWithInterpolator(Number progress);
-
- T getCurrent();
-}
diff --git a/forge-port-api/src/main/resources/META-INF/mods.toml b/forge-port-api/src/main/resources/META-INF/mods.toml
deleted file mode 100644
index 3cd0d4e..0000000
--- a/forge-port-api/src/main/resources/META-INF/mods.toml
+++ /dev/null
@@ -1,28 +0,0 @@
-modLoader="javafml"
-loaderVersion="${loader_version_range}"
-license="${mod_license}"
-
-[[mods]]
-modId="${mod_id}"
-version="${mod_version}"
-displayName="${mod_name}"
-displayTest="IGNORE_ALL_VERSION"
-authors="Reiasu"
-description='''
-ReiParticlesAPI runtime for Forge 1.20.x.
-Includes animation, emitter, display and test lifecycle services used by ReiParticleSkill.
-'''
-
-[[dependencies.${mod_id}]]
-modId="forge"
-mandatory=true
-versionRange="${loader_version_range}"
-ordering="NONE"
-side="BOTH"
-
-[[dependencies.${mod_id}]]
-modId="minecraft"
-mandatory=true
-versionRange="${minecraft_version_range}"
-ordering="NONE"
-side="BOTH"
diff --git a/forge-port-api/src/main/resources/pack.mcmeta b/forge-port-api/src/main/resources/pack.mcmeta
deleted file mode 100644
index 73613b9..0000000
--- a/forge-port-api/src/main/resources/pack.mcmeta
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "pack": {
- "pack_format": 15,
- "description": "ReiParticlesAPI resources"
- }
-}
\ No newline at end of file
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/ReiParticlesAPISchedulerTest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/ReiParticlesAPISchedulerTest.java
deleted file mode 100644
index 6f03d10..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/ReiParticlesAPISchedulerTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi;
-
-import org.junit.jupiter.api.Test;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class ReiParticlesAPISchedulerTest {
-
- private ReiParticlesAPI.Scheduler createScheduler() {
- return new ReiParticlesAPI.Scheduler();
- }
-
- @Test
- void shouldExecuteScheduledTask() {
- ReiParticlesAPI.Scheduler scheduler = createScheduler();
- AtomicBoolean executed = new AtomicBoolean(false);
-
- scheduler.runTask(1, () -> executed.set(true));
-
- // tick 1: currentTick becomes 1, executionTick is 1 → should fire
- scheduler.tick();
- assertTrue(executed.get());
- }
-
- @Test
- void shouldExecuteWhenTickIsZero() {
- ReiParticlesAPI.Scheduler scheduler = createScheduler();
- AtomicBoolean executed = new AtomicBoolean(false);
-
- // runTask(0) clamps to 1, so executionTick = 0 + 1 = 1
- scheduler.runTask(0, () -> executed.set(true));
-
- // Before tick: not yet executed
- assertFalse(executed.get());
- // tick 1: should fire
- scheduler.tick();
- assertTrue(executed.get());
- }
-}
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/ReiParticlesAPITest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/ReiParticlesAPITest.java
deleted file mode 100644
index 6fe760f..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/ReiParticlesAPITest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class ReiParticlesAPITest {
- @Test
- void shouldAllowRepeatedLifecycleCalls() {
- assertDoesNotThrow(ReiParticlesAPI::init);
- assertDoesNotThrow(ReiParticlesAPI::init);
- assertTrue(ReiParticlesAPI.isInitialized());
-
- assertDoesNotThrow(() -> ReiParticlesAPI.INSTANCE.loadScannerPackages());
- assertDoesNotThrow(() -> ReiParticlesAPI.INSTANCE.loadScannerPackages());
- assertTrue(ReiParticlesAPI.INSTANCE.scannersLoaded());
-
- assertDoesNotThrow(() -> ReiParticlesAPI.INSTANCE.registerTest());
- assertDoesNotThrow(() -> ReiParticlesAPI.INSTANCE.registerTest());
- assertTrue(ReiParticlesAPI.INSTANCE.testHooksRegistered());
- }
-}
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/animation/AnimateRuntimeTest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/animation/AnimateRuntimeTest.java
deleted file mode 100644
index aaac104..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/animation/AnimateRuntimeTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi.animation;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class AnimateRuntimeTest {
- @AfterEach
- void cleanup() {
- AnimateManager.INSTANCE.clear();
- }
-
- @Test
- void shouldDriveAnimateToCompletionByServerTicks() {
- CountingAction action = new CountingAction(3);
- Animate animate = new Animate().addNode(new AnimateNode().addAction(action));
-
- AnimateManager.INSTANCE.displayAnimateServer(animate);
- assertEquals(1, AnimateManager.INSTANCE.activeCount());
-
- AnimateManager.INSTANCE.tickServer();
- AnimateManager.INSTANCE.tickServer();
- assertEquals(1, AnimateManager.INSTANCE.activeCount());
-
- AnimateManager.INSTANCE.tickServer();
- assertEquals(1, AnimateManager.INSTANCE.activeCount());
- AnimateManager.INSTANCE.tickServer();
- assertEquals(0, AnimateManager.INSTANCE.activeCount());
- assertTrue(animate.getDone());
- assertEquals(3, action.ticks);
- }
-
- @Test
- void shouldCancelWhenPredicateIsSatisfied() {
- CountingAction action = new CountingAction(20);
- Animate animate = new Animate()
- .addNode(new AnimateNode().addAction(action))
- .addCancelPredicate(a -> action.ticks >= 2);
-
- AnimateManager.INSTANCE.displayAnimateServer(animate);
- AnimateManager.INSTANCE.tickServer();
- AnimateManager.INSTANCE.tickServer();
- AnimateManager.INSTANCE.tickServer();
-
- assertTrue(animate.getDone());
- assertEquals(0, AnimateManager.INSTANCE.activeCount());
- assertTrue(action.ticks >= 2);
- }
-
- private static final class CountingAction extends AnimateAction {
- private final int maxTicks;
- private int ticks;
-
- private CountingAction(int maxTicks) {
- this.maxTicks = maxTicks;
- }
-
- @Override
- public boolean checkDone() {
- return ticks >= maxTicks;
- }
-
- @Override
- public void tick() {
- ticks++;
- }
-
- @Override
- public void onStart() {
- ticks = 0;
- }
-
- @Override
- public void onDone() {
- }
- }
-}
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/display/DisplayEntityManagerTest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/display/DisplayEntityManagerTest.java
deleted file mode 100644
index c3ad21c..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/display/DisplayEntityManagerTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi.display;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class DisplayEntityManagerTest {
- @AfterEach
- void cleanup() {
- DisplayEntityManager.INSTANCE.clear();
- }
-
- @Test
- void shouldTickAndRemoveCanceledDisplays() {
- DisplayEntityManager.INSTANCE.spawn(new DebugDisplayEntity(0.0, 64.0, 0.0, "group"));
- assertEquals(1, DisplayEntityManager.INSTANCE.activeCount());
-
- DisplayEntity entity = DisplayEntityManager.INSTANCE.getDisplays().get(0);
- entity.cancel();
- DisplayEntityManager.INSTANCE.tickAll();
-
- assertEquals(0, DisplayEntityManager.INSTANCE.activeCount());
- }
-
- @Test
- void shouldIgnoreUnknownDisplayObjects() {
- DisplayEntityManager.INSTANCE.spawn("invalid");
- assertEquals(0, DisplayEntityManager.INSTANCE.activeCount());
- }
-}
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/event/ReiEventBusTest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/event/ReiEventBusTest.java
deleted file mode 100644
index 8d06a7a..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/event/ReiEventBusTest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi.event;
-
-import com.reiasu.reiparticlesapi.annotations.events.EventHandler;
-import com.reiasu.reiparticlesapi.event.api.ReiEvent;
-import com.reiasu.reiparticlesapi.event.api.EventInterruptible;
-import com.reiasu.reiparticlesapi.event.api.EventPriority;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class ReiEventBusTest {
- @AfterEach
- void clean() {
- ReiEventBus.INSTANCE.clear();
- }
-
- @Test
- void shouldDispatchHandlersByPriorityOrder() {
- StringBuilder order = new StringBuilder();
- ReiEventBus.INSTANCE.registerListenerInstance("test", new PriorityListener(order));
-
- ReiEventBus.call(new BaseEvent());
-
- assertEquals("HIGHEST,HIGH,NORMAL,LOW,LOWEST", order.toString());
- }
-
- @Test
- void shouldStopWhenEventIsInterrupted() {
- AtomicInteger calls = new AtomicInteger();
- ReiEventBus.INSTANCE.registerListenerInstance("test", new InterruptingListener(calls));
-
- ReiEventBus.call(new InterruptEvent());
-
- assertEquals(1, calls.get());
- }
-
- @Test
- void shouldDispatchChildThenParentEventHandlers() {
- StringBuilder order = new StringBuilder();
- ReiEventBus.INSTANCE.registerListenerInstance("test", new HierarchyListener(order));
-
- ReiEventBus.call(new ChildEvent());
-
- assertEquals("child,parent", order.toString());
- }
-
- private static class BaseEvent extends ReiEvent {
- }
-
- private static final class ChildEvent extends BaseEvent {
- }
-
- private static final class InterruptEvent extends ReiEvent implements EventInterruptible {
- private boolean interrupted;
-
- @Override
- public boolean isInterrupted() {
- return interrupted;
- }
-
- @Override
- public void setInterrupted(boolean interrupted) {
- this.interrupted = interrupted;
- }
- }
-
- private static final class PriorityListener {
- private final StringBuilder order;
-
- private PriorityListener(StringBuilder order) {
- this.order = order;
- }
-
- @EventHandler(priority = EventPriority.HIGHEST)
- public void highest(BaseEvent event) {
- append("HIGHEST");
- }
-
- @EventHandler(priority = EventPriority.HIGH)
- public void high(BaseEvent event) {
- append("HIGH");
- }
-
- @EventHandler(priority = EventPriority.NORMAL)
- public void normal(BaseEvent event) {
- append("NORMAL");
- }
-
- @EventHandler(priority = EventPriority.LOW)
- public void low(BaseEvent event) {
- append("LOW");
- }
-
- @EventHandler(priority = EventPriority.LOWEST)
- public void lowest(BaseEvent event) {
- append("LOWEST");
- }
-
- private void append(String value) {
- if (order.length() > 0) {
- order.append(',');
- }
- order.append(value);
- }
- }
-
- private static final class InterruptingListener {
- private final AtomicInteger calls;
-
- private InterruptingListener(AtomicInteger calls) {
- this.calls = calls;
- }
-
- @EventHandler(priority = EventPriority.HIGHEST)
- public void first(InterruptEvent event) {
- calls.incrementAndGet();
- event.setInterrupted(true);
- }
-
- @EventHandler(priority = EventPriority.LOWEST)
- public void second(InterruptEvent event) {
- calls.incrementAndGet();
- }
- }
-
- private static final class HierarchyListener {
- private final StringBuilder order;
-
- private HierarchyListener(StringBuilder order) {
- this.order = order;
- }
-
- @EventHandler
- public void onChild(ChildEvent event) {
- append("child");
- }
-
- @EventHandler
- public void onParent(BaseEvent event) {
- append("parent");
- }
-
- private void append(String value) {
- if (order.length() > 0) {
- order.append(',');
- }
- order.append(value);
- }
- }
-}
-
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/network/buffer/ParticleControllerDataBuffersTest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/network/buffer/ParticleControllerDataBuffersTest.java
deleted file mode 100644
index 8e75245..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/network/buffer/ParticleControllerDataBuffersTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi.network.buffer;
-
-import org.junit.jupiter.api.Test;
-
-import java.util.UUID;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-class ParticleControllerDataBuffersTest {
- @Test
- void shouldRoundTripBufferWithEnvelopeEncoding() {
- StringControllerBuffer source = ParticleControllerDataBuffers.INSTANCE.string("forge-runtime");
- byte[] encoded = ParticleControllerDataBuffers.INSTANCE.encode(source);
- ParticleControllerDataBuffer> decoded = ParticleControllerDataBuffers.INSTANCE.decodeToBuffer(encoded);
-
- assertInstanceOf(StringControllerBuffer.class, decoded);
- assertEquals("forge-runtime", decoded.getLoadedValue());
- }
-
- @Test
- void shouldDecodeByBufferId() {
- UUID uuid = UUID.randomUUID();
- UUIDControllerBuffer source = ParticleControllerDataBuffers.INSTANCE.uuid(uuid);
-
- ParticleControllerDataBuffer> decoded =
- ParticleControllerDataBuffers.INSTANCE.withIdDecode(UUIDControllerBuffer.ID, source.encode());
-
- assertNotNull(decoded);
- assertEquals(uuid, decoded.getLoadedValue());
- }
-
- @Test
- void shouldResolveWrapperClassToPrimitiveRegistration() {
- ParticleControllerDataBuffer> buffer = ParticleControllerDataBuffers.INSTANCE.fromBufferType(42, Integer.class);
-
- assertNotNull(buffer);
- assertInstanceOf(IntControllerBuffer.class, buffer);
- assertEquals(42, buffer.getLoadedValue());
- }
-
- @Test
- void shouldRoundTripPrimitiveArrays() {
- int[] input = new int[]{2, 4, 6, 8};
- IntArrayControllerBuffer source = ParticleControllerDataBuffers.INSTANCE.intArray(input);
-
- ParticleControllerDataBuffer> decoded =
- ParticleControllerDataBuffers.INSTANCE.withIdDecode(IntArrayControllerBuffer.ID, source.encode());
-
- assertNotNull(decoded);
- assertArrayEquals(input, (int[]) decoded.getLoadedValue());
- }
-}
-
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/network/packet/CameraShakeS2CPacketTest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/network/packet/CameraShakeS2CPacketTest.java
deleted file mode 100644
index 01701a1..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/network/packet/CameraShakeS2CPacketTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi.network.packet;
-
-import io.netty.buffer.Unpooled;
-import net.minecraft.network.FriendlyByteBuf;
-import net.minecraft.world.phys.Vec3;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class CameraShakeS2CPacketTest {
- @Test
- void shouldEncodeAndDecodeWithoutLosingData() {
- CameraShakeS2CPacket input = new CameraShakeS2CPacket(12.5, new Vec3(1.25, 80.0, -4.5), 0.85, 40);
- FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
-
- CameraShakeS2CPacket.encode(input, buf);
- CameraShakeS2CPacket output = CameraShakeS2CPacket.decode(buf);
-
- assertEquals(input.range(), output.range());
- assertEquals(input.origin().x, output.origin().x);
- assertEquals(input.origin().y, output.origin().y);
- assertEquals(input.origin().z, output.origin().z);
- assertEquals(input.amplitude(), output.amplitude());
- assertEquals(input.tick(), output.tick());
- }
-}
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/network/particle/emitters/ParticleEmittersManagerTest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/network/particle/emitters/ParticleEmittersManagerTest.java
deleted file mode 100644
index 8da5aa6..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/network/particle/emitters/ParticleEmittersManagerTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi.network.particle.emitters;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class ParticleEmittersManagerTest {
- @AfterEach
- void cleanup() {
- ParticleEmittersManager.clear();
- }
-
- @Test
- void shouldTickAndPruneEmittersAtMaxTick() {
- CountingEmitter emitter = new CountingEmitter();
- emitter.setMaxTick(3);
- ParticleEmittersManager.spawnEmitters(emitter);
-
- assertEquals(1, ParticleEmittersManager.activeCount());
-
- ParticleEmittersManager.tickAll();
- ParticleEmittersManager.tickAll();
- assertEquals(1, ParticleEmittersManager.activeCount());
-
- ParticleEmittersManager.tickAll();
- assertEquals(0, ParticleEmittersManager.activeCount());
- assertEquals(3, emitter.emittedTicks);
- }
-
- @Test
- void shouldIgnoreNonEmitterObjects() {
- ParticleEmittersManager.spawnEmitters("not-an-emitter");
- assertEquals(0, ParticleEmittersManager.activeCount());
- }
-
- private static final class CountingEmitter extends ParticleEmitters {
- private int emittedTicks;
-
- @Override
- protected void emitTick() {
- emittedTicks++;
- }
- }
-}
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/utils/builder/PointsAndFourierBuilderTest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/utils/builder/PointsAndFourierBuilderTest.java
deleted file mode 100644
index 69d4cf2..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/utils/builder/PointsAndFourierBuilderTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi.utils.builder;
-
-import com.reiasu.reiparticlesapi.utils.RelativeLocation;
-import org.junit.jupiter.api.Test;
-
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-final class PointsAndFourierBuilderTest {
- @Test
- void fourierBuilderKeepsRequestedCount() {
- FourierSeriesBuilder builder = new FourierSeriesBuilder()
- .count(314)
- .scale(0.2857142857142857)
- .addFourier(2.0, 4.0)
- .addFourier(-5.0, -3.0);
- assertEquals(314, builder.build().size());
- }
-
- @Test
- void discreteCircleProducesExpectedSamples() {
- PointsBuilder builder = new PointsBuilder()
- .addDiscreteCircleXZ(48.0, 200, 8.0);
- assertEquals(200, builder.create().size());
- }
-
- @Test
- void createWithStyleDataMapsEachPoint() {
- PointsBuilder builder = new PointsBuilder().addCircle(10.0, 32);
- Map mapped = builder.createWithStyleData(point -> point.getX() > 0 ? "A" + point.getX() : "B" + point.getZ());
- assertFalse(mapped.isEmpty());
- assertTrue(mapped.values().stream().allMatch(point -> point != null));
- }
-}
diff --git a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/utils/helper/StyleHelperRuntimeTest.java b/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/utils/helper/StyleHelperRuntimeTest.java
deleted file mode 100644
index 394fa88..0000000
--- a/forge-port-api/src/test/java/com/reiasu/reiparticlesapi/utils/helper/StyleHelperRuntimeTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2025 Reiasu
- *
- * This file is part of ReiParticlesAPI.
- *
- * ReiParticlesAPI is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * ReiParticlesAPI is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with ReiParticlesAPI. If not, see .
- */
-// SPDX-License-Identifier: LGPL-3.0-only
-package com.reiasu.reiparticlesapi.utils.helper;
-
-import com.reiasu.reiparticlesapi.network.particle.style.ParticleGroupStyle;
-import com.reiasu.reiparticlesapi.utils.RelativeLocation;
-import com.reiasu.reiparticlesapi.utils.helper.impl.StyleStatusHelper;
-import org.junit.jupiter.api.Test;
-
-import java.util.Collections;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-final class StyleHelperRuntimeTest {
- @Test
- void statusHelperRemovesStyleAfterClosedInterval() {
- ParticleGroupStyle style = new ParticleGroupStyle() {
- @Override public Map getCurrentFrames() { return Collections.emptyMap(); }
- @Override public void onDisplay() {}
- };
- StyleStatusHelper statusHelper = HelperUtil.INSTANCE.styleStatus(3);
- statusHelper.loadController(style);
- statusHelper.setStatus(StatusHelper.Status.DISABLE);
-
- style.tick();
- style.tick();
- style.tick();
-
- assertTrue(style.getCanceled());
- }
-
- @Test
- void styleScaleHelperMovesScaleForward() {
- ParticleGroupStyle style = new ParticleGroupStyle() {
- @Override public Map getCurrentFrames() { return Collections.emptyMap(); }
- @Override public void onDisplay() {}
- };
- ScaleHelper helper = HelperUtil.INSTANCE.scaleStyle(0.1, 1.0, 10);
- helper.loadController(style);
- helper.doScale();
- helper.doScale();
- helper.doScale();
-
- assertTrue(style.getScale() > 0.1);
- assertTrue(style.getScale() <= 1.0);
- }
-
- @Test
- void bezierScaleHelperProducesBoundedScale() {
- ParticleGroupStyle style = new ParticleGroupStyle() {
- @Override public Map getCurrentFrames() { return Collections.emptyMap(); }
- @Override public void onDisplay() {}
- };
- BezierValueScaleHelper helper = HelperUtil.INSTANCE.bezierValueScaleStyle(
- 0.01,
- 1.0,
- 20,
- new RelativeLocation(17.0, 1.0, 0.0),
- new RelativeLocation(-3.0, 0.0, 0.0)
- );
- helper.loadController(style);
- for (int i = 0; i < 8; i++) {
- helper.doScale();
- }
-
- assertTrue(style.getScale() >= 0.01);
- assertTrue(style.getScale() <= 1.0);
- }
-}
diff --git a/forge-port-api/wrapper-bootstrap.gradle b/forge-port-api/wrapper-bootstrap.gradle
deleted file mode 100644
index e2b3c3e..0000000
--- a/forge-port-api/wrapper-bootstrap.gradle
+++ /dev/null
@@ -1,4 +0,0 @@
-tasks.register('wrapper', Wrapper) {
- gradleVersion = '8.10'
- distributionType = Wrapper.DistributionType.ALL
-}
\ No newline at end of file
diff --git a/forge-port/build.gradle b/forge-port/build.gradle
index 8658105..351e6f8 100644
--- a/forge-port/build.gradle
+++ b/forge-port/build.gradle
@@ -2,7 +2,7 @@ plugins {
id 'eclipse'
id 'idea'
id 'maven-publish'
- id 'net.minecraftforge.gradle' version '[6.0.36,6.2)'
+ id 'net.neoforged.moddev' version '2.0.140'
}
version = mod_version
@@ -14,78 +14,50 @@ base {
java {
toolchain {
- languageVersion = JavaLanguageVersion.of(17)
+ languageVersion = JavaLanguageVersion.of(21)
}
}
-// Merged dev output for the reiparticlesapi module.
-// Combines API classes + resources into one directory so Forge discovers it as a mod.
-def reiApiMergedDir = file("${buildDir}/reiapi-dev")
-
-// Source set whose output points to the merged dir. No actual compilation —
-// the Sync task (prepareReiApiDev) populates reiApiMergedDir instead.
-sourceSets {
- reiapi {
- java.srcDirs = []
- resources.srcDirs = []
- }
-}
-sourceSets.reiapi.output.resourcesDir = reiApiMergedDir
-
-minecraft {
- mappings channel: 'official', version: minecraft_version
+sourceSets.main.resources { srcDir 'src/generated/resources' }
- copyIdeResources = true
+neoForge {
+ version = neo_version
runs {
- configureEach {
- workingDirectory project.file('run')
- property 'forge.logging.markers', 'REGISTRIES'
- property 'forge.logging.console.level', 'debug'
- mods {
- "${mod_id}" {
- source sourceSets.main
- }
- reiparticlesapi {
- source sourceSets.reiapi
- }
- }
+ client {
+ client()
}
-
- client {}
-
server {
- args '--nogui'
+ server()
+ programArgument '--nogui'
}
-
data {
- args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
+ data()
+ programArguments.addAll '--mod', mod_id, '--all',
+ '--output', file('src/generated/resources/').absolutePath,
+ '--existing', file('src/main/resources/').absolutePath
}
}
-}
-sourceSets.main.resources { srcDir 'src/generated/resources' }
+ mods {
+ "reiparticlesapi" {
+ sourceSet(sourceSets.main)
+ }
+ "${mod_id}" {
+ sourceSet(sourceSets.main)
+ }
+ }
+}
repositories {
- maven { url = 'https://maven.minecraftforge.net' }
+ maven { url = 'https://maven.neoforged.net/releases' }
mavenCentral()
}
-// Sync task: merge API classes + processed resources into a single directory for dev runtime
-tasks.register('prepareReiApiDev', Sync) {
- from '../forge-port-api/build/classes/java/main'
- from '../forge-port-api/build/resources/main'
- into reiApiMergedDir
-}
-['runClient', 'runServer', 'runData'].each { taskName ->
- tasks.matching { it.name == taskName }.configureEach { dependsOn 'prepareReiApiDev' }
-}
-
dependencies {
- minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
- // Resolved via composite build (see settings.gradle includeBuild)
- compileOnly 'com.reiasu.reiparticlesapi:reiparticlesapi:1.0-SNAPSHOT-forge-port'
- testImplementation 'com.reiasu.reiparticlesapi:reiparticlesapi:1.0-SNAPSHOT-forge-port'
+ // ClassGraph — runtime classpath scanning (used by ReiAPIScanner)
+ implementation 'io.github.classgraph:classgraph:4.8.174'
+
testImplementation platform('org.junit:junit-bom:5.11.4')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
@@ -100,11 +72,12 @@ processResources {
mod_name : mod_name,
mod_license : mod_license,
mod_version : mod_version,
+ neo_version_range : neo_version_range,
loader_version_range : loader_version_range,
minecraft_version_range: minecraft_version_range
]
inputs.properties replaceProperties
- filesMatching(['META-INF/mods.toml']) {
+ filesMatching(['META-INF/neoforge.mods.toml']) {
expand replaceProperties
}
}
diff --git a/forge-port/gradle.properties b/forge-port/gradle.properties
index 4cdeceb..9c386b8 100644
--- a/forge-port/gradle.properties
+++ b/forge-port/gradle.properties
@@ -1,3 +1,4 @@
+org.gradle.java.home=C:\\Program Files\\Microsoft\\jdk-21.0.10.7-hotspot
org.gradle.jvmargs=-Xmx2G
org.gradle.daemon=true
org.gradle.parallel=true
@@ -5,13 +6,14 @@ org.gradle.parallel=true
mod_id=reiparticleskill
mod_name=ReiParticleSkill
mod_license=LGPL-3.0-only
-mod_version=1.0-SNAPSHOT-forge-port
+mod_version=1.0-SNAPSHOT-neoforge
mod_group_id=com.reiasu.reiparticleskill
-minecraft_version=1.20.1
-forge_version=47.2.0
-loader_version_range=[47,)
-minecraft_version_range=[1.20.1,1.21)
+minecraft_version=1.21.1
+neo_version=21.1.173
+neo_version_range=[21.1,)
+loader_version_range=[4,)
+minecraft_version_range=[1.21.1,1.22)
org.gradle.java.installations.auto-download=true
org.gradle.java.installations.auto-detect=true
diff --git a/forge-port/settings.gradle b/forge-port/settings.gradle
index 2ca17ed..6c2e2ec 100644
--- a/forge-port/settings.gradle
+++ b/forge-port/settings.gradle
@@ -1,7 +1,7 @@
pluginManagement {
repositories {
gradlePluginPortal()
- maven { url = 'https://maven.minecraftforge.net' }
+ maven { url = 'https://maven.neoforged.net/releases' }
mavenCentral()
}
}
@@ -10,11 +10,6 @@ plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
}
-rootProject.name = 'reiparticleskill-forge-port'
+rootProject.name = 'reiparticleskill-neoforge'
-// Composite build: resolve forge-port-api from the sibling project instead of a pre-built jar.
-includeBuild('../forge-port-api') {
- dependencySubstitution {
- substitute module('com.reiasu.reiparticlesapi:reiparticlesapi') using project(':')
- }
-}
+// forge-port-api merged into this module — single jar build
diff --git a/forge-port/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesAPI.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesAPI.java
new file mode 100644
index 0000000..7065016
--- /dev/null
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesAPI.java
@@ -0,0 +1,73 @@
+package com.reiasu.reiparticlesapi;
+
+import net.neoforged.neoforge.common.NeoForge;
+import com.reiasu.reiparticlesapi.event.ReiEventBus;
+import com.reiasu.reiparticlesapi.event.api.ReiEvent;
+import com.reiasu.reiparticlesapi.network.particle.emitters.ParticleEmittersManager;
+import com.reiasu.reiparticlesapi.scheduler.ReiScheduler;
+import com.reiasu.reiparticlesapi.test.SimpleTestGroupBuilder;
+import com.reiasu.reiparticlesapi.test.TestManager;
+import net.minecraft.server.level.ServerPlayer;
+public final class ReiParticlesAPI {
+ public static final ReiParticlesAPI INSTANCE = new ReiParticlesAPI();
+ private static boolean initialized;
+ private static boolean scannersLoaded;
+ private static boolean testHooksRegistered;
+
+ private ReiParticlesAPI() {
+ }
+
+ public static void init() {
+ if (initialized) return;
+ initialized = true;
+ ParticleEmittersManager.registerBuiltinCodecs();
+ }
+
+ public static boolean isInitialized() {
+ return initialized;
+ }
+
+ public void loadScannerPackages() {
+ if (scannersLoaded) return;
+ scannersLoaded = true;
+ ReiEventBus.INSTANCE.initListeners();
+ }
+
+ public boolean scannersLoaded() {
+ return scannersLoaded;
+ }
+
+ public void registerTest() {
+ if (testHooksRegistered) return;
+ testHooksRegistered = true;
+ TestManager.INSTANCE.register("api-test-group-builder", user -> buildSmokeTestGroup(user));
+ }
+
+ public boolean testHooksRegistered() {
+ return testHooksRegistered;
+ }
+
+ public void appendEventListenerTarget(String modId, String packageName) {
+ ReiEventBus.INSTANCE.appendListenerTarget(modId, packageName);
+ }
+
+ public void initEventListeners() {
+ ReiEventBus.INSTANCE.initListeners();
+ }
+
+ public void registerEventListener(String modId, Object listener) {
+ ReiEventBus.INSTANCE.registerListenerInstance(modId, listener);
+ }
+
+ public T callEvent(T event) {
+ return NeoForge.EVENT_BUS.post(event);
+ }
+
+ public static ReiScheduler reiScheduler() {
+ return ReiScheduler.INSTANCE;
+ }
+
+ private static SimpleTestGroupBuilder buildSmokeTestGroup(ServerPlayer user) {
+ return new SimpleTestGroupBuilder("api-test-group-builder", user);
+ }
+}
diff --git a/forge-port/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesAPIForge.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesAPIForge.java
new file mode 100644
index 0000000..304d11b
--- /dev/null
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesAPIForge.java
@@ -0,0 +1,183 @@
+package com.reiasu.reiparticlesapi;
+
+import com.reiasu.reiparticlesapi.animation.AnimateManager;
+import com.reiasu.reiparticlesapi.client.ClientTickEventForwarder;
+import com.reiasu.reiparticlesapi.commands.APICommand;
+import com.reiasu.reiparticlesapi.config.APIConfig;
+import com.reiasu.reiparticlesapi.display.DisplayEntityManager;
+import com.reiasu.reiparticlesapi.event.ForgeEventForwarder;
+import com.reiasu.reiparticlesapi.event.ReiEventBus;
+import com.reiasu.reiparticlesapi.event.events.server.ServerPostTickEvent;
+import com.reiasu.reiparticlesapi.event.events.server.ServerPreTickEvent;
+import com.reiasu.reiparticlesapi.network.ReiParticlesNetwork;
+import com.reiasu.reiparticlesapi.network.animation.PathMotionManager;
+import com.reiasu.reiparticlesapi.network.particle.composition.manager.ParticleCompositionManager;
+import com.reiasu.reiparticlesapi.network.particle.emitters.ParticleEmittersManager;
+import com.reiasu.reiparticlesapi.network.particle.emitters.environment.wind.WindDirections;
+import com.reiasu.reiparticlesapi.network.particle.emitters.type.EmittersShootTypes;
+import com.reiasu.reiparticlesapi.network.particle.style.ParticleStyleManager;
+import com.reiasu.reiparticlesapi.particles.ControllableParticleEffectManager;
+import com.reiasu.reiparticlesapi.particles.ReiModParticles;
+import com.reiasu.reiparticlesapi.particles.control.group.ClientParticleGroupManager;
+import com.reiasu.reiparticlesapi.particles.impl.particles.*;
+import com.reiasu.reiparticlesapi.renderer.client.ClientRenderEntityManager;
+import com.reiasu.reiparticlesapi.renderer.server.ServerRenderEntityManager;
+import com.reiasu.reiparticlesapi.scheduler.ReiScheduler;
+import com.reiasu.reiparticlesapi.test.TestManager;
+import com.reiasu.reiparticlesapi.utils.ClientCameraUtil;
+import com.mojang.logging.LogUtils;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.bus.api.IEventBus;
+import net.neoforged.fml.ModContainer;
+import net.neoforged.fml.common.Mod;
+import net.neoforged.fml.config.ModConfig;
+import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
+import net.neoforged.fml.loading.FMLEnvironment;
+import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent;
+import net.neoforged.neoforge.common.NeoForge;
+import net.neoforged.neoforge.event.RegisterCommandsEvent;
+import net.neoforged.neoforge.client.event.ClientTickEvent;
+import net.neoforged.neoforge.event.tick.ServerTickEvent;
+import org.slf4j.Logger;
+
+@Mod(ReiParticlesAPIForge.MOD_ID)
+public final class ReiParticlesAPIForge {
+ public static final String MOD_ID = "reiparticlesapi";
+ private static final Logger LOGGER = LogUtils.getLogger();
+
+ private record TickHandler(String name, Runnable action) {}
+ private static final int ERROR_THROTTLE_TICKS = 200; // ~10 seconds at 20 tps
+ private static final int[] errorCooldowns = new int[32]; // indexed per handler slot
+ private static int tickCounter;
+
+ private final TickHandler[] clientHandlers;
+ private final TickHandler[] serverHandlers;
+ public ReiParticlesAPIForge(IEventBus modBus, ModContainer container) {
+ clientHandlers = buildClientHandlers();
+ serverHandlers = buildServerHandlers(serverRef);
+ registerConfig(container);
+ registerTickCallbacks(modBus);
+ registerCommands();
+ initSystems(modBus);
+
+
+ }
+ private void registerConfig(ModContainer container) {
+ container.registerConfig(ModConfig.Type.COMMON, APIConfig.SPEC);
+ }
+
+ private void registerTickCallbacks(IEventBus modBus) {
+ modBus.addListener((FMLClientSetupEvent event) -> onClientSetup());
+ ReiModParticles.register(modBus);
+ modBus.addListener(this::onRegisterParticleProviders);
+
+ NeoForge.EVENT_BUS.addListener((ClientTickEvent.Pre event) -> {
+ if (FMLEnvironment.dist == Dist.CLIENT) {
+ ClientTickEventForwarder.onClientStartTick();
+ }
+ });
+ NeoForge.EVENT_BUS.addListener((ClientTickEvent.Post event) -> onClientEndTick());
+ NeoForge.EVENT_BUS.addListener((ServerTickEvent.Pre event) ->
+ NeoForge.EVENT_BUS.post(new ServerPreTickEvent(event.getServer())));
+ NeoForge.EVENT_BUS.addListener((ServerTickEvent.Post event) ->
+ onServerEndTick(event.getServer()));
+ }
+
+ private void registerCommands() {
+ NeoForge.EVENT_BUS.addListener(
+ (RegisterCommandsEvent event) ->
+ APICommand.INSTANCE.register(event.getDispatcher())
+ );
+ }
+
+ private void initSystems(IEventBus modBus) {
+ modBus.addListener(ReiParticlesNetwork::registerPayloads);
+ ParticleEmittersManager.registerBuiltinCodecs();
+ EmittersShootTypes.INSTANCE.init();
+ WindDirections.INSTANCE.init();
+ ControllableParticleEffectManager.INSTANCE.init();
+ ReiParticlesAPI.init();
+ ReiParticlesAPI.INSTANCE.loadScannerPackages();
+ ReiParticlesAPI.INSTANCE.registerTest();
+ }
+ private TickHandler[] buildClientHandlers() {
+ return new TickHandler[] {
+ new TickHandler("ClientTickEventForwarder", ClientTickEventForwarder::onClientEndTick),
+ new TickHandler("AnimateManager.client", () -> AnimateManager.INSTANCE.tickClient()),
+ new TickHandler("ParticleEmittersManager.client", ParticleEmittersManager::tickClient),
+ new TickHandler("DisplayEntityManager.client", () -> DisplayEntityManager.INSTANCE.tickClient()),
+ new TickHandler("ParticleCompositionManager.client", () -> ParticleCompositionManager.INSTANCE.tickClient()),
+ new TickHandler("ParticleStyleManager.client", ParticleStyleManager::doTickClient),
+ new TickHandler("ClientParticleGroupManager", () -> ClientParticleGroupManager.INSTANCE.doClientTick()),
+ new TickHandler("ClientRenderEntityManager", () -> ClientRenderEntityManager.INSTANCE.doClientTick()),
+ new TickHandler("PathMotionManager", () -> PathMotionManager.INSTANCE.tick()),
+ new TickHandler("ReiScheduler", () -> ReiScheduler.INSTANCE.doTick()),
+ new TickHandler("ClientCameraUtil", () -> ClientCameraUtil.INSTANCE.tick()),
+ };
+ }
+
+ private TickHandler[] buildServerHandlers(net.minecraft.server.MinecraftServer[] serverRef) {
+ return new TickHandler[] {
+ new TickHandler("AnimateManager.server", () -> AnimateManager.INSTANCE.tickServer()),
+ new TickHandler("ParticleEmittersManager.server", ParticleEmittersManager::tickAll),
+ new TickHandler("DisplayEntityManager.server", () -> DisplayEntityManager.INSTANCE.tickAll()),
+ new TickHandler("ParticleCompositionManager.server", () -> ParticleCompositionManager.INSTANCE.tickAll()),
+ new TickHandler("ParticleStyleManager.server", ParticleStyleManager::doTickServer),
+ new TickHandler("ServerRenderEntityManager.tick", () -> ServerRenderEntityManager.INSTANCE.tick()),
+ new TickHandler("ServerRenderEntityManager.upgrade", () -> ServerRenderEntityManager.INSTANCE.upgrade(serverRef[0])),
+ new TickHandler("TestManager", () -> TestManager.INSTANCE.doTickServer()),
+ new TickHandler("ReiScheduler.server", () -> ReiScheduler.INSTANCE.doTick()),
+ new TickHandler("ServerPostTickEvent", () -> NeoForge.EVENT_BUS.post(new ServerPostTickEvent(serverRef[0]))),
+ };
+ }
+
+ private void onClientEndTick() {
+ tickCounter++;
+ runHandlers(clientHandlers, 0);
+ }
+
+ private void onServerEndTick(net.minecraft.server.MinecraftServer server) {
+ serverRef[0] = server;
+ runHandlers(serverHandlers, clientHandlers.length);
+ }
+
+ private final net.minecraft.server.MinecraftServer[] serverRef = new net.minecraft.server.MinecraftServer[1];
+
+ private static void runHandlers(TickHandler[] handlers, int cooldownOffset) {
+ for (int i = 0; i < handlers.length; i++) {
+ try {
+ handlers[i].action.run();
+ } catch (Exception e) {
+ int slot = cooldownOffset + i;
+ if (errorCooldowns[slot] <= 0) {
+ errorCooldowns[slot] = ERROR_THROTTLE_TICKS;
+ LOGGER.warn("Tick handler '{}' threw (throttled to once per ~10s):",
+ handlers[i].name, e);
+ } else {
+ errorCooldowns[slot]--;
+ }
+ }
+ }
+ }
+ private void onClientSetup() {
+
+ }
+
+ private void onRegisterParticleProviders(RegisterParticleProvidersEvent event) {
+ event.registerSpriteSet(ReiModParticles.CONTROLLABLE_END_ROD.get(),
+ ControllableEndRodParticle.Factory::new);
+ event.registerSpriteSet(ReiModParticles.CONTROLLABLE_ENCHANTMENT.get(),
+ ControllableEnchantmentParticle.Factory::new);
+ event.registerSpriteSet(ReiModParticles.CONTROLLABLE_CLOUD.get(),
+ ControllableCloudParticle.Factory::new);
+ event.registerSpriteSet(ReiModParticles.CONTROLLABLE_FLASH.get(),
+ ControllableFlashParticle.Factory::new);
+ event.registerSpriteSet(ReiModParticles.CONTROLLABLE_FIREWORK.get(),
+ ControllableFireworkParticle.Factory::new);
+ event.registerSpecial(ReiModParticles.CONTROLLABLE_FALLING_DUST.get(),
+ new ControllableFallingDustParticle.Factory());
+ event.registerSpriteSet(ReiModParticles.CONTROLLABLE_SPLASH.get(),
+ ControllableSplashParticle.Factory::new);
+
+ }
+}
\ No newline at end of file
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesConstants.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesConstants.java
similarity index 75%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesConstants.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesConstants.java
index 7b3602b..1f8255a 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesConstants.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/ReiParticlesConstants.java
@@ -1,13 +1,8 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/**
- * Shared constants for the ReiParticles API.
- */
public final class ReiParticlesConstants {
public static final String MOD_ID = "reiparticlesapi";
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/Animate.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/Animate.java
similarity index 77%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/Animate.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/Animate.java
index b7b5ef8..85f2d7a 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/Animate.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/Animate.java
@@ -1,29 +1,9 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.animation;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
-/**
- * A sequential animation state machine that plays a chain of {@link AnimateNode}s.
- *
- * Nodes are added via {@link #addNode(AnimateNode, int)} with an optional interval
- * (in ticks) between the current node finishing and the next starting.
- * Call {@link #start()} once, then {@link #tick()} every game tick.
- *
- * Cancel predicates can abort the entire chain early via
- * {@link #addCancelPredicate(java.util.function.Predicate)}.
- *
- * Key state accessors:
- *
- *
{@link #getTimestamp()} — ticks elapsed since the current node started
- *
{@link #getCurrentIndex()} — index of the current node in the chain
- *
{@link #getDone()} — whether the animation has finished or been cancelled
- *
{@link #getDisplay()} — whether the animation is currently running
- *
- */
public class Animate {
private final List nodes = new ArrayList<>();
private final List> cancelPredicates = new ArrayList<>();
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateAction.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateAction.java
similarity index 93%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateAction.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateAction.java
index 2f5d0bf..0b44dd2 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateAction.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateAction.java
@@ -1,5 +1,3 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.animation;
public abstract class AnimateAction {
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateManager.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateManager.java
similarity index 97%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateManager.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateManager.java
index 79b94a3..f2ec99c 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateManager.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateManager.java
@@ -1,5 +1,3 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.animation;
import java.util.ArrayList;
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateNode.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateNode.java
similarity index 96%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateNode.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateNode.java
index fa3e4b8..7ffe71a 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateNode.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/AnimateNode.java
@@ -1,5 +1,3 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.animation;
import java.util.ArrayList;
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/AngleAnimator.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/AngleAnimator.java
similarity index 80%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/AngleAnimator.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/AngleAnimator.java
index 0fa795e..a42ce8f 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/AngleAnimator.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/AngleAnimator.java
@@ -1,11 +1,5 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.animation.timeline;
-/**
- * Animates an angle value over a given duration using an easing function.
- * Provides delta-based progress for both "glow" (forward) and "fade" (reverse) animations.
- */
public final class AngleAnimator {
private final int durationTicks;
private final double targetAngle;
@@ -28,10 +22,7 @@ public boolean getFinished() {
return this.finished;
}
- /**
- * Returns the angle delta for a forward (growing) animation step.
- */
- public double glowDelta() {
+ public double glowDelta() {
if (this.finished) {
return 0.0;
}
@@ -47,10 +38,7 @@ public double glowDelta() {
return delta;
}
- /**
- * Returns the angle delta for a reverse (fading) animation step.
- */
- public double fadeDelta() {
+ public double fadeDelta() {
if (this.finished && this.tick >= this.durationTicks) {
this.finished = false;
this.tick = 0;
diff --git a/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Ease.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Ease.java
new file mode 100644
index 0000000..721c51a
--- /dev/null
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Ease.java
@@ -0,0 +1,6 @@
+package com.reiasu.reiparticlesapi.animation.timeline;
+
+@FunctionalInterface
+public interface Ease {
+ double cal(double t);
+}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Eases.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Eases.java
similarity index 87%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Eases.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Eases.java
index e82f266..d683619 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Eases.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Eases.java
@@ -1,14 +1,9 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.animation.timeline;
import com.reiasu.reiparticlesapi.utils.RelativeLocation;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.Vec2;
-/**
- * Collection of standard easing functions and factory methods.
- */
public final class Eases {
public static final Eases INSTANCE = new Eases();
@@ -105,11 +100,7 @@ public static Ease outBounce() {
return outBounce(7.5625, 2.75);
}
- /**
- * Create a cubic bezier easing function using RelativeLocation handles.
- * Start at (0,0), end at (1,1). startHandle and endHandle define the two control points.
- */
- public Ease bezierEase(RelativeLocation startHandle, RelativeLocation endHandle) {
+ public Ease bezierEase(RelativeLocation startHandle, RelativeLocation endHandle) {
RelativeLocation target = new RelativeLocation(1, 1, 0);
RelativeLocation end = new RelativeLocation(
target.getX() + endHandle.getX(),
@@ -129,10 +120,7 @@ public Ease bezierEase(RelativeLocation startHandle, RelativeLocation endHandle)
};
}
- /**
- * Create a cubic bezier easing function using Vec3 handles.
- */
- public Ease bezierEase(Vec3 startHandle, Vec3 endHandle) {
+ public Ease bezierEase(Vec3 startHandle, Vec3 endHandle) {
Vec3 target = new Vec3(1.0, 1.0, 0.0);
Vec3 end = target.add(endHandle);
return x -> {
@@ -148,10 +136,7 @@ public Ease bezierEase(Vec3 startHandle, Vec3 endHandle) {
};
}
- /**
- * Create a cubic bezier easing function using Vec2 handles.
- */
- public Ease bezierEase(Vec2 startHandle, Vec2 endHandle) {
+ public Ease bezierEase(Vec2 startHandle, Vec2 endHandle) {
Vec2 target = new Vec2(1.0f, 1.0f);
Vec2 end = new Vec2(target.x + endHandle.x, target.y + endHandle.y);
return x -> {
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Timeline.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Timeline.java
similarity index 70%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Timeline.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Timeline.java
index af01a0b..e830ad5 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Timeline.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/animation/timeline/Timeline.java
@@ -1,16 +1,9 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.animation.timeline;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.function.BooleanSupplier;
-/**
- * A simple sequential step-based timeline.
- * Each step is a BooleanSupplier that returns true when the step is done.
- * On each tick, the current step is polled; when it returns true, the next step begins.
- */
public final class Timeline {
private final Deque steps = new ArrayDeque<>();
diff --git a/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/CodecField.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/CodecField.java
new file mode 100644
index 0000000..f78cc14
--- /dev/null
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/CodecField.java
@@ -0,0 +1,12 @@
+package com.reiasu.reiparticlesapi.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CodecField {
+ int index() default 0;
+}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/ReiAutoRegister.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/ReiAutoRegister.java
similarity index 56%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/ReiAutoRegister.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/ReiAutoRegister.java
index 44943b2..46e4685 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/ReiAutoRegister.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/ReiAutoRegister.java
@@ -1,5 +1,3 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.annotations;
import java.lang.annotation.ElementType;
@@ -7,10 +5,6 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * Marks a class for automatic registration by the ReiParticles API scanner.
- * Classes annotated with this will be discovered and registered at mod init time.
- */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ReiAutoRegister {
diff --git a/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/BufferCodec.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/BufferCodec.java
new file mode 100644
index 0000000..e91e7ce
--- /dev/null
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/BufferCodec.java
@@ -0,0 +1,34 @@
+package com.reiasu.reiparticlesapi.annotations.codec;
+
+import net.minecraft.network.FriendlyByteBuf;
+
+public interface BufferCodec {
+
+ static BufferCodec of(Encoder encoder, Decoder decoder) {
+ return new BufferCodec() {
+ @Override
+ public void encode(FriendlyByteBuf buf, T value) {
+ encoder.encode(buf, value);
+ }
+
+ @Override
+ public T decode(FriendlyByteBuf buf) {
+ return decoder.decode(buf);
+ }
+ };
+ }
+
+ void encode(FriendlyByteBuf buf, T value);
+
+ T decode(FriendlyByteBuf buf);
+
+ @FunctionalInterface
+ interface Encoder {
+ void encode(FriendlyByteBuf buf, T value);
+ }
+
+ @FunctionalInterface
+ interface Decoder {
+ T decode(FriendlyByteBuf buf);
+ }
+}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/CodecHelper.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/CodecHelper.java
similarity index 84%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/CodecHelper.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/CodecHelper.java
index b5eb31e..05bde94 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/CodecHelper.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/codec/CodecHelper.java
@@ -1,5 +1,3 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.annotations.codec;
import com.reiasu.reiparticlesapi.annotations.CodecField;
@@ -26,13 +24,6 @@
import java.util.HashMap;
import java.util.Map;
-/**
- * Singleton codec registry for serializing annotated {@link CodecField} fields
- * to/from {@link FriendlyByteBuf}.
- *
- * Replaces the original Fabric implementation that used {@code StreamCodec}
- * (which does not exist in Forge 1.20.1). Instead uses {@link BufferCodec}.
- */
public final class CodecHelper {
public static final CodecHelper INSTANCE = new CodecHelper();
@@ -47,18 +38,10 @@ public Map> getSupposedTypes() {
return supposedTypes;
}
- /**
- * Registers a codec for the given type.
- */
public void register(Class type, BufferCodec codec) {
supposedTypes.put(type.getName(), codec);
}
- /**
- * Copies all {@link CodecField}-annotated, non-final fields from {@code other}
- * to {@code current} via reflection. Both objects must be the same class.
- * Fields are processed in stable {@link CodecField#index()} order.
- */
public void updateFields(Object current, Object other) {
if (current == null || other == null) return;
if (!current.getClass().equals(other.getClass())) return;
@@ -73,12 +56,6 @@ public void updateFields(Object current, Object other) {
}
}
- /**
- * Encodes all {@link CodecField}-annotated fields of {@code obj} into {@code buf},
- * in stable {@link CodecField#index()} order.
- *
- * @throws IllegalStateException if a field's type has no registered codec
- */
@SuppressWarnings("unchecked")
public void encodeAnnotatedFields(FriendlyByteBuf buf, Object obj) {
for (Field field : getCodecFields(obj.getClass())) {
@@ -95,12 +72,6 @@ public void encodeAnnotatedFields(FriendlyByteBuf buf, Object obj) {
}
}
- /**
- * Decodes all {@link CodecField}-annotated fields of {@code obj} from {@code buf},
- * in stable {@link CodecField#index()} order.
- *
- * @throws IllegalStateException if a field's type has no registered codec
- */
@SuppressWarnings("unchecked")
public void decodeAnnotatedFields(FriendlyByteBuf buf, Object obj) {
for (Field field : getCodecFields(obj.getClass())) {
@@ -117,10 +88,7 @@ public void decodeAnnotatedFields(FriendlyByteBuf buf, Object obj) {
}
}
- /**
- * Returns {@link CodecField}-annotated non-final fields sorted by
- * {@link CodecField#index()} then by field name for deterministic ordering.
- */
+ // TODO: cache this per class, reflection every call is slow
private static List getCodecFields(Class> clazz) {
List result = new ArrayList<>();
for (Field f : clazz.getDeclaredFields()) {
@@ -133,8 +101,6 @@ private static List getCodecFields(Class> clazz) {
return result;
}
- // ────────────────── Static registration of built-in types ──────────────────
-
static {
registerPrimitives();
registerArrays();
@@ -143,11 +109,6 @@ private static List getCodecFields(Class> clazz) {
registerMinecraftTypes();
registerProjectTypes();
registerRangeTypes();
- // NOTE: ControllableParticleData, SimpleRandomParticleData, ItemStack,
- // and all InterpolatorXxx types should register themselves via
- // CodecHelper.INSTANCE.register(MyClass.class, myCodec)
- // in their own static initializers or during mod init, since their
- // codecs are not yet available as static fields in Forge 1.20.1.
}
private static void registerPrimitives() {
@@ -194,7 +155,7 @@ private static void registerPrimitives() {
private static void registerArrays() {
INSTANCE.register(byte[].class, BufferCodec.of(
- FriendlyByteBuf::writeByteArray, FriendlyByteBuf::readByteArray));
+ (FriendlyByteBuf buf, byte[] v) -> buf.writeByteArray(v), buf -> buf.readByteArray()));
INSTANCE.register(long[].class, BufferCodec.of(
FriendlyByteBuf::writeLongArray, FriendlyByteBuf::readLongArray));
INSTANCE.register(int[].class, BufferCodec.of(
@@ -212,12 +173,12 @@ private static void registerArrays() {
private static void registerJavaTypes() {
INSTANCE.register(String.class, BufferCodec.of(
- FriendlyByteBuf::writeUtf,
- FriendlyByteBuf::readUtf
+ (FriendlyByteBuf buf, String v) -> buf.writeUtf(v),
+ (FriendlyByteBuf buf) -> buf.readUtf()
));
INSTANCE.register(UUID.class, BufferCodec.of(
- FriendlyByteBuf::writeUUID,
- FriendlyByteBuf::readUUID
+ (FriendlyByteBuf buf, UUID v) -> buf.writeUUID(v),
+ buf -> buf.readUUID()
));
}
diff --git a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/composition/handler/ParticleCompositionHelper.java b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/composition/handler/ParticleCompositionHelper.java
similarity index 69%
rename from forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/composition/handler/ParticleCompositionHelper.java
rename to forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/composition/handler/ParticleCompositionHelper.java
index af0a266..098ddf6 100644
--- a/forge-port-api/src/main/java/com/reiasu/reiparticlesapi/annotations/composition/handler/ParticleCompositionHelper.java
+++ b/forge-port/src/main/java/com/reiasu/reiparticlesapi/annotations/composition/handler/ParticleCompositionHelper.java
@@ -1,5 +1,3 @@
-// SPDX-License-Identifier: LGPL-3.0-only
-// Copyright (C) 2025 Reiasu
package com.reiasu.reiparticlesapi.annotations.composition.handler;
import com.reiasu.reiparticlesapi.annotations.CodecField;
@@ -18,16 +16,6 @@
import java.util.Comparator;
import java.util.List;
-/**
- * Singleton helper that auto-generates {@link BufferCodec} instances for
- * {@link ParticleComposition} subclasses by scanning {@link CodecField}
- * annotated fields via reflection.
- *
- * Fields are sorted by name for deterministic encode/decode order.
- * The codec also handles the base composition fields via
- * {@link ParticleComposition#encodeBase}/{@link ParticleComposition#decodeBase}
- * (and the sequenced variant for {@link SequencedParticleComposition} subclasses).
- */
public final class ParticleCompositionHelper {
public static final ParticleCompositionHelper INSTANCE = new ParticleCompositionHelper();
@@ -35,17 +23,7 @@ public final class ParticleCompositionHelper {
private ParticleCompositionHelper() {
}
- /**
- * Generates a {@link BufferCodec} for the concrete composition type.
- *
- * The composition class must have a constructor {@code (Vec3, Level)} for
- * reflective instantiation during decode.
- *
- * @param randomInstance any instance of the target composition class
- * (used only to determine the concrete type)
- * @return a codec that encodes/decodes the full composition state
- */
- public BufferCodec generateCodec(ParticleComposition randomInstance) {
+ public BufferCodec generateCodec(ParticleComposition randomInstance) {
Class> type = randomInstance.getClass();
// Resolve constructor (Vec3, Level) for reflective instantiation
@@ -64,7 +42,7 @@ public BufferCodec generateCodec(ParticleComposition random
);
}
- // ─── Encode ──────────────────────────────────────────────────────────
+ // --”€--”€--”€ Encode --”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€
private static void encodeComposition(Class> type, FriendlyByteBuf buf,
ParticleComposition composition) {
@@ -89,7 +67,7 @@ private static void encodeComposition(Class> type, FriendlyByteBuf buf,
}
}
- // ─── Decode ──────────────────────────────────────────────────────────
+ // --”€--”€--”€ Decode --”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€
private static ParticleComposition decodeComposition(Constructor> constructor,
Class> type,
@@ -126,12 +104,9 @@ private static ParticleComposition decodeComposition(Constructor> constructor,
return instance;
}
- // ─── Utilities ───────────────────────────────────────────────────────
+ // --”€--”€--”€ Utilities --”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€--”€
- /**
- * Collects all non-final fields annotated with {@link CodecField}, sorted by name.
- */
- private static List getCodecFields(Class> type) {
+ private static List getCodecFields(Class> type) {
Field[] allFields = type.getDeclaredFields();
List result = new ArrayList<>();
for (Field f : allFields) {
@@ -143,16 +118,13 @@ private static List getCodecFields(Class> type) {
return result;
}
- /**
- * Looks up the registered codec for the given type, throwing if not found.
- */
- @SuppressWarnings("unchecked")
+ @SuppressWarnings("unchecked")
private static BufferCodec