SCHEDULED_BLOOM_RENDERS = new ArrayList<>();
+
+ private static final ReentrantLock BLOOM_RENDER_LOCK = new ReentrantLock();
+
+ /**
+ *
+ * Register a custom bloom render callback for subsequent world render. The render call persists until the
+ * {@code blockEntity} is invalidated, or the world associated with {@code blockEntity} or the ticket is
+ * manually freed by calling {@link BloomRenderTicket#invalidate()}.
+ *
+ *
+ * This method does not register bloom render ticket when Iris/Oculus is present, and an invalid ticket will be
+ * returned instead.
+ *
+ *
+ * @param setup Render setup, if exists
+ * @param bloomType Type of the bloom
+ * @param render Rendering callback
+ * @param blockEntity Meta tile entity instance
+ * @return Ticket for the registered bloom render callback
+ * @throws NullPointerException if {@code bloomType == null || render == null || blockEntity == null}
+ */
+ @NotNull
+ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup,
+ @NotNull BloomType bloomType,
+ @NotNull IBloomEffect render,
+ @NotNull BlockEntity blockEntity) {
+ Objects.requireNonNull(blockEntity, "blockEntity == null");
+ return registerBloomRender(setup, bloomType,
+ new IBloomEffect() {
+
+ @Override
+ public void renderBloomEffect(@NotNull PoseStack poseStack, @NotNull BufferBuilder buffer,
+ @NotNull EffectRenderContext context) {
+ render.renderBloomEffect(poseStack, buffer, context);
+ }
+
+ @Override
+ public boolean shouldRenderBloomEffect(@NotNull EffectRenderContext context) {
+ return blockEntity.getLevel() == context.renderViewEntity().level() &&
+ render.shouldRenderBloomEffect(context);
+ }
+ },
+ t -> !blockEntity.isRemoved(),
+ blockEntity::getLevel);
+ }
+
+ /**
+ *
+ * Register a custom bloom render callback for subsequent world render. The render call persists until the
+ * {@code particle} is invalidated, or the ticket is manually freed by calling
+ * {@link BloomRenderTicket#invalidate()}.
+ *
+ *
+ * This method does not register bloom render ticket when Iris/Oculus is present, and an invalid ticket will be
+ * returned instead.
+ *
+ *
+ * @param setup Render setup, if exists
+ * @param bloomType Type of the bloom
+ * @param render Rendering callback
+ * @param particle Particle instance
+ * @return Ticket for the registered bloom render callback
+ * @throws NullPointerException if {@code bloomType == null || render == null || metaTileEntity == null}
+ */
+ @NotNull
+ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup,
+ @NotNull BloomType bloomType,
+ @NotNull IBloomEffect render,
+ @NotNull GTParticle particle) {
+ Objects.requireNonNull(particle, "particle == null");
+ return registerBloomRender(setup, bloomType, render, t -> particle.isAlive());
+ }
+
+ /**
+ *
+ * Register a custom bloom render callback for subsequent world render. The render call persists until it is
+ * manually freed by calling {@link BloomRenderTicket#invalidate()}, or invalidated by validity checker.
+ *
+ *
+ * This method does not register bloom render ticket when Iris/Oculus is present, and an invalid ticket will be
+ * returned instead.
+ *
+ *
+ * @param setup Render setup, if exists
+ * @param bloomType Type of the bloom
+ * @param render Rendering callback
+ * @param validityChecker Optional validity checker; returning {@code false} causes the ticket to be invalidated.
+ * Checked on both pre-/post-render each frame.
+ * @return Ticket for the registered bloom render callback
+ * @throws NullPointerException if {@code bloomType == null || render == null}
+ * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, BlockEntity)
+ * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, GTParticle)
+ * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, Predicate, Supplier)
+ */
+ @NotNull
+ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup,
+ @NotNull BloomType bloomType,
+ @NotNull IBloomEffect render,
+ @Nullable Predicate validityChecker) {
+ return registerBloomRender(setup, bloomType, render, validityChecker, null);
+ }
+
+ /**
+ *
+ * Register a custom bloom render callback for subsequent world render. The render call persists until it is
+ * manually freed by calling {@link BloomRenderTicket#invalidate()}, or invalidated by validity checker.
+ *
+ *
+ * This method does not register bloom render ticket when Iris/Oculus is present, and an invalid ticket will be
+ * returned instead.
+ *
+ *
+ * @param setup Render setup, if exists
+ * @param bloomType Type of the bloom
+ * @param render Rendering callback
+ * @param validityChecker Optional validity checker; returning {@code false} causes the ticket to be invalidated.
+ * Checked on both pre/post render each frame.
+ * @param worldContext Optional world bound to the ticket. If the world returned is not null, the bloom ticket
+ * will be automatically invalidated on world unload. If world context returns {@code null},
+ * it will not be affected by aforementioned automatic invalidation.
+ * @return Ticket for the registered bloom render callback
+ * @throws NullPointerException if {@code bloomType == null || render == null}
+ * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, BlockEntity)
+ * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, GTParticle)
+ */
+ @NotNull
+ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup,
+ @NotNull BloomType bloomType,
+ @NotNull IBloomEffect render,
+ @Nullable Predicate validityChecker,
+ @Nullable Supplier worldContext) {
+ if (!GTShaders.allowedShader()) return BloomRenderTicket.INVALID;
+ BloomRenderTicket ticket = new BloomRenderTicket(setup, bloomType, render, validityChecker, worldContext);
+ BLOOM_RENDER_LOCK.lock();
+ try {
+ SCHEDULED_BLOOM_RENDERS.add(ticket);
+ } finally {
+ BLOOM_RENDER_LOCK.unlock();
+ }
+ return ticket;
+ }
+
+ /**
+ * Invalidate tickets associated with given level.
+ *
+ * @param level the level that was unloaded
+ */
+ public static void invalidateLevelTickets(@NotNull LevelAccessor level) {
+ Objects.requireNonNull(level, "level == null");
+ BLOOM_RENDER_LOCK.lock();
+ try {
+ for (BloomRenderTicket ticket : SCHEDULED_BLOOM_RENDERS) {
+ if (ticket.isValid() && ticket.worldContext != null && ticket.worldContext.get() == level) {
+ ticket.invalidate();
+ }
+ }
+
+ for (Map.Entry> e : BLOOM_RENDERS.entrySet()) {
+ for (BloomRenderTicket ticket : e.getValue()) {
+ if (ticket.isValid() && ticket.worldContext != null && ticket.worldContext.get() == level) {
+ ticket.invalidate();
+ }
+ }
+ }
+ } finally {
+ BLOOM_RENDER_LOCK.unlock();
+ }
+ }
+
+ public static void init() {}
+
+ public static final AtomicBoolean isDrawingBlockBloom = new AtomicBoolean(false);
+
+ public static void renderBloom(double camX, double camY, double camZ,
+ PoseStack poseStack, Matrix4f projectionMatrix,
+ Frustum frustum,
+ float partialTicks,
+ @NotNull Entity entity) {
+ if (!GTShaders.allowedShader()) {
+ return;
+ }
+ Minecraft.getInstance().getProfiler().popPush("gtceu_block_bloom");
+
+ BLOOM_RENDER_LOCK.lock();
+ try {
+ preDraw();
+
+ GTShaders.BLOOM_TARGET.bindWrite(false);
+
+ EffectRenderContext context = EffectRenderContext.getInstance()
+ .update(entity, camX, camY, camZ, frustum, partialTicks);
+
+ GTRenderTypes.getBloom().setupRenderState();
+
+ if (!ConfigHolder.INSTANCE.client.shader.emissiveTexturesBloom) {
+ RenderSystem.depthMask(true);
+
+ if (!BLOOM_RENDERS.isEmpty()) {
+ for (List list : BLOOM_RENDERS.values()) {
+ BufferBuilder buffer = Tesselator.getInstance().getBuilder();
+ draw(poseStack, buffer, context, list);
+ }
+ }
+ postDraw();
+ RenderSystem.depthMask(false);
+
+ render(partialTicks, poseStack, projectionMatrix, camX, camY, camZ);
+ return;
+ }
+
+ BloomEffect.strength = ConfigHolder.INSTANCE.client.shader.strength;
+ BloomEffect.baseBrightness = ConfigHolder.INSTANCE.client.shader.baseBrightness;
+ BloomEffect.highBrightnessThreshold = ConfigHolder.INSTANCE.client.shader.highBrightnessThreshold;
+ BloomEffect.lowBrightnessThreshold = ConfigHolder.INSTANCE.client.shader.lowBrightnessThreshold;
+ BloomEffect.step = ConfigHolder.INSTANCE.client.shader.step;
+
+ // ********** render custom bloom ************
+
+ RenderSystem.depthMask(true);
+ if (!BLOOM_RENDERS.isEmpty()) {
+ for (var e : BLOOM_RENDERS.entrySet()) {
+ List list = e.getValue();
+ BufferBuilder buffer = Tesselator.getInstance().getBuilder();
+ draw(poseStack, buffer, context, list);
+ }
+ }
+ RenderSystem.depthMask(false);
+
+ isDrawingBlockBloom.set(true);
+ render(partialTicks, poseStack, projectionMatrix, camX, camY, camZ);
+ isDrawingBlockBloom.set(false);
+
+ postDraw();
+ GTRenderTypes.getBloom().clearRenderState();
+ } finally {
+ BLOOM_RENDER_LOCK.unlock();
+ }
+ }
+
+ private static void preDraw() {
+ for (BloomRenderTicket ticket : SCHEDULED_BLOOM_RENDERS) {
+ if (!ticket.isValid()) continue;
+ BLOOM_RENDERS.computeIfAbsent(new BloomRenderKey(ticket.renderSetup, ticket.bloomType),
+ k -> new ArrayList<>()).add(ticket);
+ }
+ SCHEDULED_BLOOM_RENDERS.clear();
+ }
+
+ private static void draw(@NotNull PoseStack poseStack,
+ @NotNull BufferBuilder buffer, @NotNull EffectRenderContext context,
+ @NotNull List tickets) {
+ boolean initialized = false;
+ @Nullable
+ IRenderSetup renderSetup = null;
+ for (BloomRenderTicket ticket : tickets) {
+ ticket.checkValidity();
+ if (!ticket.isValid() || !ticket.render.shouldRenderBloomEffect(context)) continue;
+ if (!initialized) {
+ initialized = true;
+ renderSetup = ticket.renderSetup;
+ if (renderSetup != null) {
+ renderSetup.preDraw(buffer);
+ }
+ }
+ ticket.render.renderBloomEffect(poseStack, buffer, context);
+ }
+ if (initialized && renderSetup != null) {
+ renderSetup.postDraw(buffer);
+ }
+ }
+
+ private static void postDraw() {
+ for (var it = BLOOM_RENDERS.values().iterator(); it.hasNext();) {
+ List list = it.next();
+
+ if (!list.isEmpty()) {
+ if (!list.removeIf(ticket -> {
+ ticket.checkValidity();
+ return !ticket.isValid();
+ }) || !list.isEmpty()) continue;
+ }
+
+ it.remove();
+ }
+ }
+
+ public static void uploadBloomBuffer(BufferBuilder.RenderedBuffer builder, VertexBuffer buffer) {
+ if (!buffer.isInvalid()) {
+ buffer.bind();
+ buffer.upload(builder);
+ VertexBuffer.unbind();
+ }
+ }
+
+ public static void removeBloomChunk(BlockPos origin) {
+ GTShaders.BLOOM_BUFFER_BUILDERS.remove(origin);
+ VertexBuffer buffer = GTShaders.BLOOM_BUFFERS.remove(origin);
+ if (buffer != null) {
+ if (!RenderSystem.isOnRenderThread()) {
+ RenderSystem.recordRenderCall(buffer::close);
+ } else {
+ buffer.close();
+ }
+ }
+ GTShaders.RENDERED_BLOOM_BUFFERS.remove(origin);
+ }
+
+ public static BufferBuilder getOrStartBloomBuffer(BlockPos pos) {
+ BufferBuilder builder = GTShaders.BLOOM_BUFFER_BUILDERS.computeIfAbsent(pos,
+ $ -> new BufferBuilder(GTRenderTypes.getBloom().bufferSize()));
+ if (!builder.building()) {
+ builder.begin(GTRenderTypes.getBloom().mode(), GTRenderTypes.getBloom().format());
+ }
+ return builder;
+ }
+
+ public static void bakeBloomChunkBuffers(BlockPos pos) {
+ if (!GTShaders.allowedShader()) {
+ return;
+ }
+
+ BufferBuilder builder = GTShaders.BLOOM_BUFFER_BUILDERS.get(pos);
+ if (builder == null || !builder.building()) {
+ return;
+ }
+ BufferBuilder.RenderedBuffer buffer = builder.endOrDiscardIfEmpty();
+ if (buffer != null) {
+ GTShaders.RENDERED_BLOOM_BUFFERS.put(pos, buffer);
+ if (RenderSystem.isOnRenderThread()) {
+ VertexBuffer vertexBuffer = GTShaders.BLOOM_BUFFERS.computeIfAbsent(pos,
+ $ -> new VertexBuffer(VertexBuffer.Usage.STATIC));
+ BloomEffectUtil.uploadBloomBuffer(buffer, vertexBuffer);
+ } else {
+ RenderSystem.recordRenderCall(() -> {
+ VertexBuffer vertexBuffer = GTShaders.BLOOM_BUFFERS.computeIfAbsent(pos,
+ $ -> new VertexBuffer(VertexBuffer.Usage.STATIC));
+ BloomEffectUtil.uploadBloomBuffer(buffer, vertexBuffer);
+ });
+ }
+ }
+ }
+
+ public static ThreadLocal CURRENT_RENDERING_CHUNK_POS = new ThreadLocal<>();
+
+ private static final String UNREAL_COMPOSITE_SHADER_NAME = "gtceu:unreal_composite";
+ private static final String UNITY_COMPOSITE_SHADER_NAME = "gtceu:unity_composite";
+ private static final String SEPERABLE_BLUR_SHADER_NAME = "gtceu:seperable_blur";
+ private static final String BLOOM_INTENSIVE_UNIFORM = "BloomIntensive";
+ private static final String BLOOM_BASE_UNIFORM = "BloomBase";
+ private static final String BLOOM_THRESHOLD_UP_UNIFORM = "BloomThresholdUp";
+ private static final String BLOOM_THRESHOLD_DOWN_UNIFORM = "BloomThresholdDown";
+ private static final String BLUR_DIR_UNIFORM = "BlurDir";
+
+ private static void render(float partialTicks, PoseStack poseStack, Matrix4f projectionMatrix, double camX,
+ double camY, double camZ) {
+ RenderSystem.enableBlend();
+ RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
+ // Forcefully insert config values to shader
+ List passes = ((PostChainAccessor) GTShaders.BLOOM_CHAIN).getPasses();
+ for (PostPass pass : passes) {
+ EffectInstance shader = pass.getEffect();
+ shader.safeGetUniform("iTime").set(GTShaders.getITime(partialTicks));
+ shader.safeGetUniform("EnableFilter").set(BloomEffectUtil.isDrawingBlockBloom.get() ? 1 : 0);
+
+ if (GTShaders.BLOOM_TYPE == BloomType.UNREAL) {
+ if (shader.getName().equals(SEPERABLE_BLUR_SHADER_NAME)) {
+ int index = passes.indexOf(pass);
+ switch (index) {
+ case 1, 3, 5, 7 -> shader.safeGetUniform(BLUR_DIR_UNIFORM).set(BloomEffect.step, 0.0f);
+ case 2, 4, 6, 8 -> shader.safeGetUniform(BLUR_DIR_UNIFORM).set(0.0f, BloomEffect.step);
+ }
+ }
+ }
+ if (shader.getName().equals(UNITY_COMPOSITE_SHADER_NAME) ||
+ shader.getName().equals(UNREAL_COMPOSITE_SHADER_NAME)) {
+ shader.safeGetUniform(BLOOM_INTENSIVE_UNIFORM).set(BloomEffect.strength);
+ shader.safeGetUniform(BLOOM_BASE_UNIFORM).set(BloomEffect.baseBrightness);
+ shader.safeGetUniform(BLOOM_THRESHOLD_UP_UNIFORM).set(BloomEffect.highBrightnessThreshold);
+ shader.safeGetUniform(BLOOM_THRESHOLD_DOWN_UNIFORM).set(BloomEffect.lowBrightnessThreshold);
+ }
+ }
+
+ for (var entry : GTShaders.BLOOM_BUFFERS.entrySet()) {
+ // return early if buffer is invalid or has no vertex data bound
+ // VertexBuffer#mode's nullness is the easiest way to check this.
+ if (entry.getValue().isInvalid() || ((VertexBufferAccessor) entry.getValue()).getMode() == null) {
+ continue;
+ }
+ entry.getValue().bind();
+ poseStack.pushPose();
+ poseStack.translate(entry.getKey().getX(), entry.getKey().getY(), entry.getKey().getZ());
+ poseStack.translate(-camX, -camY, -camZ);
+ entry.getValue().drawWithShader(poseStack.last().pose(), projectionMatrix, RenderSystem.getShader());
+ poseStack.popPose();
+ }
+
+ GTShaders.BLOOM_CHAIN.process(partialTicks);
+ RenderSystem.disableBlend();
+ RenderSystem.defaultBlendFunc();
+ VertexBuffer.unbind();
+ Minecraft.getInstance().getMainRenderTarget().bindWrite(false);
+ }
+
+ private record BloomRenderKey(@Nullable IRenderSetup renderSetup, @NotNull BloomType bloomType) {
+
+ }
+
+ public static final class BloomRenderTicket {
+
+ public static final BloomRenderTicket INVALID = new BloomRenderTicket();
+
+ @Nullable
+ private final IRenderSetup renderSetup;
+ private final BloomType bloomType;
+ private final IBloomEffect render;
+ @Nullable
+ private final Predicate validityChecker;
+ @Nullable
+ private final Supplier worldContext;
+
+ private boolean invalidated;
+
+ BloomRenderTicket() {
+ this(null, BloomType.DISABLED, (p, b, c) -> {}, null, null);
+ this.invalidated = true;
+ }
+
+ BloomRenderTicket(@Nullable IRenderSetup renderSetup, @NotNull BloomType bloomType,
+ @NotNull IBloomEffect render, @Nullable Predicate validityChecker,
+ @Nullable Supplier worldContext) {
+ this.renderSetup = renderSetup;
+ this.bloomType = Objects.requireNonNull(bloomType, "bloomType == null");
+ this.render = Objects.requireNonNull(render, "render == null");
+ this.validityChecker = validityChecker;
+ this.worldContext = worldContext;
+ }
+
+ public boolean isValid() {
+ return !this.invalidated;
+ }
+
+ public void invalidate() {
+ this.invalidated = true;
+ }
+
+ private void checkValidity() {
+ if (!this.invalidated && this.validityChecker != null && !this.validityChecker.test(this)) {
+ invalidate();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/BloomUtils.java b/src/main/java/com/gregtechceu/gtceu/client/util/BloomUtils.java
deleted file mode 100644
index 6325e1f708..0000000000
--- a/src/main/java/com/gregtechceu/gtceu/client/util/BloomUtils.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.gregtechceu.gtceu.client.util;
-
-import com.lowdragmc.shimmer.client.postprocessing.PostProcessing;
-
-import net.minecraft.client.renderer.MultiBufferSource;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.api.distmarker.OnlyIn;
-
-import java.util.function.Consumer;
-
-@OnlyIn(Dist.CLIENT)
-public class BloomUtils {
-
- public static void entityBloom(Consumer sourceConsumer) {
- // Shimmer will call PostProcessing.BLOOM_UNREAL.renderEntityPost in LevelRenderer#renderLevel
- // We probably don't need to call it ourselves
- PostProcessing.BLOOM_UNREAL.postEntity(sourceConsumer);
- }
-}
diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/DrawUtil.java b/src/main/java/com/gregtechceu/gtceu/client/util/DrawUtil.java
index 33971070d4..9d1c4473e8 100644
--- a/src/main/java/com/gregtechceu/gtceu/client/util/DrawUtil.java
+++ b/src/main/java/com/gregtechceu/gtceu/client/util/DrawUtil.java
@@ -63,4 +63,22 @@ private static void fillHorizontalGradient(GuiGraphics graphics, VertexConsumer
consumer.vertex(matrix4f, (float) x2, (float) y2, (float) z).color(r2, g2, b2, a2).endVertex();
consumer.vertex(matrix4f, (float) x2, (float) y1, (float) z).color(r2, g2, b2, a2).endVertex();
}
+
+ public static int interpolateColor(int color1, int color2, float blend) {
+ int a1 = color1 >> 24 & 255;
+ int r1 = color1 >> 16 & 255;
+ int g1 = color1 >> 8 & 255;
+ int b1 = color1 & 255;
+
+ int a2 = color2 >> 24 & 255;
+ int r2 = color2 >> 16 & 255;
+ int g2 = color2 >> 8 & 255;
+ int b2 = color2 & 255;
+
+ int a = (int) (a1 * (1 - blend) + a2 * blend);
+ int r = (int) (r1 * (1 - blend) + r2 * blend);
+ int g = (int) (g1 * (1 - blend) + g2 * blend);
+ int b = (int) (b1 * (1 - blend) + b2 * blend);
+ return a << 24 | r << 16 | g << 8 | b;
+ }
}
diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/EffectRenderContext.java b/src/main/java/com/gregtechceu/gtceu/client/util/EffectRenderContext.java
new file mode 100644
index 0000000000..11657bfb43
--- /dev/null
+++ b/src/main/java/com/gregtechceu/gtceu/client/util/EffectRenderContext.java
@@ -0,0 +1,83 @@
+package com.gregtechceu.gtceu.client.util;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.culling.Frustum;
+import net.minecraft.util.Mth;
+import net.minecraft.world.entity.Entity;
+
+import lombok.Getter;
+import lombok.experimental.Accessors;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Objects;
+
+/**
+ * Collection of various information for rendering purposes.
+ */
+@Accessors(fluent = true)
+public final class EffectRenderContext {
+
+ private static final EffectRenderContext instance = new EffectRenderContext();
+
+ public static EffectRenderContext getInstance() {
+ return instance;
+ }
+
+ @Getter
+ private Frustum frustum = new Frustum(Minecraft.getInstance().levelRenderer.getFrustum());
+
+ @Nullable
+ private Entity renderViewEntity;
+ @Getter
+ private float partialTicks;
+ @Getter
+ private double cameraX;
+ @Getter
+ private double cameraY;
+ @Getter
+ private double cameraZ;
+ @Getter
+ private float rotationX;
+ @Getter
+ private float rotationZ;
+ @Getter
+ private float rotationYZ;
+ @Getter
+ private float rotationXY;
+ @Getter
+ private float rotationXZ;
+
+ @NotNull
+ public EffectRenderContext update(@NotNull Entity renderViewEntity,
+ double camX, double camY, double camZ,
+ Frustum frustum, float partialTicks) {
+ this.renderViewEntity = renderViewEntity;
+ this.partialTicks = partialTicks;
+
+ this.cameraX = camX;
+ this.cameraY = camY;
+ this.cameraZ = camZ;
+
+ float i = 1 - (Minecraft.getInstance().options.getCameraType().isFirstPerson() ? 0 : 2);
+ float pitch = renderViewEntity.getYRot();
+ float yaw = renderViewEntity.getXRot();
+ this.rotationX = Mth.cos(yaw * Mth.DEG_TO_RAD) * i;
+ this.rotationZ = Mth.sin(yaw * Mth.DEG_TO_RAD) * i;
+ this.rotationYZ = -this.rotationX * Mth.sin(pitch * Mth.DEG_TO_RAD) * i;
+ this.rotationXY = this.rotationZ * Mth.sin(pitch * Mth.DEG_TO_RAD) * i;
+ this.rotationXZ = Mth.cos(pitch * Mth.DEG_TO_RAD);
+
+ this.frustum = frustum;
+
+ return this;
+ }
+
+ /**
+ * @return render view entity
+ */
+ @NotNull
+ public Entity renderViewEntity() {
+ return Objects.requireNonNull(renderViewEntity, "renderViewEntity not available yet");
+ }
+}
diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/IBloomEffect.java b/src/main/java/com/gregtechceu/gtceu/client/util/IBloomEffect.java
new file mode 100644
index 0000000000..a956606007
--- /dev/null
+++ b/src/main/java/com/gregtechceu/gtceu/client/util/IBloomEffect.java
@@ -0,0 +1,40 @@
+package com.gregtechceu.gtceu.client.util;
+
+import com.gregtechceu.gtceu.client.renderer.IRenderSetup;
+import com.gregtechceu.gtceu.client.shader.post.BloomType;
+
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+
+import com.mojang.blaze3d.vertex.BufferBuilder;
+import com.mojang.blaze3d.vertex.PoseStack;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Render callback interface for
+ * {@link BloomEffectUtil#registerBloomRender(IRenderSetup, BloomType, IBloomEffect, BlockEntity)}.
+ */
+@FunctionalInterface
+public interface IBloomEffect {
+
+ /**
+ * Render the bloom effect.
+ *
+ * @param buffer buffer builder
+ * @param context render context
+ */
+ @OnlyIn(Dist.CLIENT)
+ void renderBloomEffect(@NotNull PoseStack poseStack, @NotNull BufferBuilder buffer,
+ @NotNull EffectRenderContext context);
+
+ /**
+ * @param context render context
+ * @return if this effect should be rendered; returning {@code false} skips
+ * {@link #renderBloomEffect(PoseStack, BufferBuilder, EffectRenderContext)} call.
+ */
+ @OnlyIn(Dist.CLIENT)
+ default boolean shouldRenderBloomEffect(@NotNull EffectRenderContext context) {
+ return true;
+ }
+}
diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/RenderBufferHelper.java b/src/main/java/com/gregtechceu/gtceu/client/util/RenderBufferHelper.java
index 5acfe490fb..ac5189d994 100644
--- a/src/main/java/com/gregtechceu/gtceu/client/util/RenderBufferHelper.java
+++ b/src/main/java/com/gregtechceu/gtceu/client/util/RenderBufferHelper.java
@@ -2,9 +2,11 @@
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
+import net.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
+import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import org.joml.Matrix4f;
@@ -88,4 +90,118 @@ public static void renderRing(PoseStack poseStack, VertexConsumer buffer, float
}
}
+
+ public static void renderCubeFrame(BufferBuilder buffer, double minX, double minY, double minZ, double maxX,
+ double maxY, double maxZ, float r, float g, float b, float a) {
+ buffer.vertex(minX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(maxX, minY, minZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(minX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(minX, maxY, minZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(minX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(minX, minY, maxZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(maxX, maxY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(minX, maxY, maxZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(maxX, maxY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(maxX, minY, maxZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(maxX, maxY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(maxX, maxY, minZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(minX, maxY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(minX, maxY, maxZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(minX, maxY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(maxX, maxY, minZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(maxX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(maxX, minY, maxZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(maxX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(maxX, maxY, minZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(minX, minY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(maxX, minY, maxZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(minX, minY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(minX, maxY, maxZ).color(r, g, b, a).endVertex();
+ }
+
+ public static void renderCubeFace(PoseStack poseStack, VertexConsumer buffer, AABB cuboid, float r, float g,
+ float b, float a,
+ boolean shade) {
+ renderCubeFace(poseStack, buffer,
+ (float) cuboid.minX, (float) cuboid.minY, (float) cuboid.minZ,
+ (float) cuboid.maxX, (float) cuboid.maxY, (float) cuboid.maxZ,
+ r, g, b, a, shade);
+ }
+
+ public static void renderCubeFace(PoseStack poseStack, VertexConsumer buffer,
+ float minX, float minY, float minZ,
+ float maxX, float maxY, float maxZ,
+ float red, float green, float blue, float alpha) {
+ renderCubeFace(poseStack, buffer, minX, minY, minZ, maxX, maxY, maxZ, red, green, blue, alpha, false);
+ }
+
+ public static void renderCubeFace(PoseStack poseStack, VertexConsumer buffer,
+ float minX, float minY, float minZ,
+ float maxX, float maxY, float maxZ,
+ float red, float green, float blue, float a,
+ boolean shade) {
+ Matrix4f pose = poseStack.last().pose();
+ float r = red, g = green, b = blue;
+
+ if (shade) {
+ r *= 0.6f;
+ g *= 0.6f;
+ b *= 0.6f;
+ }
+ buffer.vertex(pose, minX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, minX, minY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, minX, maxY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, minX, maxY, minZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(pose, maxX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, maxY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, maxY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, minY, maxZ).color(r, g, b, a).endVertex();
+
+ if (shade) {
+ r = red * 0.5f;
+ g = green * 0.5f;
+ b = blue * 0.5f;
+ }
+ buffer.vertex(pose, minX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, minY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, minX, minY, maxZ).color(r, g, b, a).endVertex();
+
+ if (shade) {
+ r = red;
+ g = green;
+ b = blue;
+ }
+ buffer.vertex(pose, minX, maxY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, minX, maxY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, maxY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, maxY, minZ).color(r, g, b, a).endVertex();
+
+ if (shade) {
+ r = red * 0.8f;
+ g = green * 0.8f;
+ b = blue * 0.8f;
+ }
+ buffer.vertex(pose, minX, minY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, minX, maxY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, maxY, minZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, minY, minZ).color(r, g, b, a).endVertex();
+
+ buffer.vertex(pose, minX, minY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, minY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, maxX, maxY, maxZ).color(r, g, b, a).endVertex();
+ buffer.vertex(pose, minX, maxY, maxZ).color(r, g, b, a).endVertex();
+ }
}
diff --git a/src/main/java/com/gregtechceu/gtceu/common/block/BatteryBlock.java b/src/main/java/com/gregtechceu/gtceu/common/block/BatteryBlock.java
index ad0f8e0fee..fcafb9f41b 100644
--- a/src/main/java/com/gregtechceu/gtceu/common/block/BatteryBlock.java
+++ b/src/main/java/com/gregtechceu/gtceu/common/block/BatteryBlock.java
@@ -3,11 +3,19 @@
import com.gregtechceu.gtceu.api.GTValues;
import com.gregtechceu.gtceu.api.block.AppearanceBlock;
import com.gregtechceu.gtceu.api.machine.multiblock.IBatteryData;
+import com.gregtechceu.gtceu.utils.FormattingUtil;
+import net.minecraft.network.chat.Component;
import net.minecraft.util.StringRepresentable;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.TooltipFlag;
+import net.minecraft.world.level.BlockGetter;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
public class BatteryBlock extends AppearanceBlock {
@@ -19,6 +27,18 @@ public BatteryBlock(Properties properties, IBatteryData data) {
this.data = data;
}
+ @Override
+ public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip,
+ TooltipFlag flag) {
+ super.appendHoverText(stack, level, tooltip, flag);
+ if (this.data.getTier() == -1) {
+ tooltip.add(Component.translatable("block.gtceu.substation_capacitor.tooltip_empty"));
+ } else {
+ tooltip.add(Component.translatable("block.gtceu.substation_capacitor.tooltip_filled",
+ FormattingUtil.formatNumbers(this.data.getCapacity())));
+ }
+ }
+
public enum BatteryPartType implements StringRepresentable, IBatteryData {
EMPTY_TIER_I,
diff --git a/src/main/java/com/gregtechceu/gtceu/common/block/FluidPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/block/FluidPipeBlock.java
index dd6664d182..88b8b38aba 100644
--- a/src/main/java/com/gregtechceu/gtceu/common/block/FluidPipeBlock.java
+++ b/src/main/java/com/gregtechceu/gtceu/common/block/FluidPipeBlock.java
@@ -16,9 +16,6 @@
import com.gregtechceu.gtceu.utils.EntityDamageUtil;
import com.gregtechceu.gtceu.utils.GTUtil;
-import com.lowdragmc.lowdraglib.side.fluid.FluidStack;
-import com.lowdragmc.lowdraglib.side.fluid.forge.FluidHelperImpl;
-
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@@ -34,6 +31,7 @@
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
+import net.minecraftforge.fluids.FluidStack;
import org.jetbrains.annotations.Nullable;
@@ -141,12 +139,11 @@ public void entityInside(BlockState state, Level level, BlockPos pos, Entity ent
int minTemperature = Integer.MAX_VALUE;
for (var tank : pipe.getFluidTanks()) {
FluidStack stack = tank.getFluid();
- net.minecraftforge.fluids.FluidStack forgeStack = FluidHelperImpl.toFluidStack(stack);
if (tank.getFluid() != null && tank.getFluid().getAmount() > 0) {
maxTemperature = Math.max(maxTemperature,
- stack.getFluid().getFluidType().getTemperature(forgeStack));
+ stack.getFluid().getFluidType().getTemperature(stack));
minTemperature = Math.min(minTemperature,
- stack.getFluid().getFluidType().getTemperature(forgeStack));
+ stack.getFluid().getFluidType().getTemperature(stack));
}
}
if (maxTemperature != Integer.MIN_VALUE) {
@@ -160,9 +157,8 @@ public void entityInside(BlockState state, Level level, BlockPos pos, Entity ent
if (tank.getFluid() != null && tank.getFluid().getAmount() > 0) {
// Apply temperature damage for the pipe (single fluid pipes)
FluidStack stack = tank.getFluid();
- net.minecraftforge.fluids.FluidStack forgeStack = FluidHelperImpl.toFluidStack(stack);
EntityDamageUtil.applyTemperatureDamage(livingEntity,
- stack.getFluid().getFluidType().getTemperature(forgeStack), 1.0F, 20);
+ stack.getFluid().getFluidType().getTemperature(stack), 1.0F, 20);
}
}
}
diff --git a/src/main/java/com/gregtechceu/gtceu/common/blockentity/CableBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/common/blockentity/CableBlockEntity.java
index ca8e848311..3d6361fd91 100644
--- a/src/main/java/com/gregtechceu/gtceu/common/blockentity/CableBlockEntity.java
+++ b/src/main/java/com/gregtechceu/gtceu/common/blockentity/CableBlockEntity.java
@@ -10,6 +10,8 @@
import com.gregtechceu.gtceu.api.item.tool.GTToolType;
import com.gregtechceu.gtceu.api.machine.TickableSubscription;
import com.gregtechceu.gtceu.api.machine.feature.IDataInfoProvider;
+import com.gregtechceu.gtceu.client.particle.GTOverheatParticle;
+import com.gregtechceu.gtceu.client.particle.GTParticleManager;
import com.gregtechceu.gtceu.common.block.CableBlock;
import com.gregtechceu.gtceu.common.data.GTBlocks;
import com.gregtechceu.gtceu.common.item.PortableScannerBehavior;
@@ -32,8 +34,13 @@
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.shapes.CollisionContext;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
@@ -60,6 +67,8 @@ public class CableBlockEntity extends PipeBlockEntity currentEnergyNet = new WeakReference<>(null);
+ @SideOnly(Side.CLIENT)
+ private GTOverheatParticle particle;
private static final int meltTemp = 3000;
private final EnumMap handlers = new EnumMap<>(Direction.class);
@@ -241,6 +250,27 @@ public boolean incrementAmperage(long amps, long voltage) {
return false;
}
+ @OnlyIn(Dist.CLIENT)
+ public boolean isParticleAlive() {
+ return particle != null && particle.isAlive();
+ }
+
+ @OnlyIn(Dist.CLIENT)
+ public void createParticle() {
+ particle = new GTOverheatParticle(this, meltTemp,
+ getPipeBlock().getShape(getBlockState(), level, getBlockPos(), CollisionContext.empty()),
+ getPipeType().insulationLevel >= 0);
+ GTParticleManager.INSTANCE.addEffect(particle);
+ }
+
+ @OnlyIn(Dist.CLIENT)
+ public void killParticle() {
+ if (isParticleAlive()) {
+ particle.setExpired();
+ particle = null;
+ }
+ }
+
public void applyHeat(int amount) {
heatQueue += amount;
if (!level.isClientSide && heatSubs == null && temperature + heatQueue > getDefaultTemp()) {
diff --git a/src/main/java/com/gregtechceu/gtceu/common/blockentity/FluidPipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/common/blockentity/FluidPipeBlockEntity.java
index 731c00a2df..7863776ce7 100644
--- a/src/main/java/com/gregtechceu/gtceu/common/blockentity/FluidPipeBlockEntity.java
+++ b/src/main/java/com/gregtechceu/gtceu/common/blockentity/FluidPipeBlockEntity.java
@@ -12,6 +12,8 @@
import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttribute;
import com.gregtechceu.gtceu.api.machine.TickableSubscription;
import com.gregtechceu.gtceu.api.machine.feature.IDataInfoProvider;
+import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank;
+import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable;
import com.gregtechceu.gtceu.common.cover.PumpCover;
import com.gregtechceu.gtceu.common.cover.data.ManualIOMode;
import com.gregtechceu.gtceu.common.item.PortableScannerBehavior;
@@ -19,15 +21,9 @@
import com.gregtechceu.gtceu.common.pipelike.fluidpipe.PipeTankList;
import com.gregtechceu.gtceu.utils.EntityDamageUtil;
import com.gregtechceu.gtceu.utils.FormattingUtil;
+import com.gregtechceu.gtceu.utils.GTTransferUtils;
import com.gregtechceu.gtceu.utils.GTUtil;
-import com.lowdragmc.lowdraglib.misc.FluidStorage;
-import com.lowdragmc.lowdraglib.side.fluid.FluidStack;
-import com.lowdragmc.lowdraglib.side.fluid.FluidTransferHelper;
-import com.lowdragmc.lowdraglib.side.fluid.IFluidTransfer;
-import com.lowdragmc.lowdraglib.side.fluid.forge.FluidHelperImpl;
-import com.lowdragmc.lowdraglib.side.fluid.forge.FluidTransferHelperImpl;
-
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@@ -53,6 +49,7 @@
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.util.LazyOptional;
+import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
import org.jetbrains.annotations.NotNull;
@@ -70,7 +67,7 @@ public class FluidPipeBlockEntity extends PipeBlockEntity tankLists = new EnumMap<>(Direction.class);
- private FluidStorage[] fluidTanks;
+ private CustomFluidTank[] fluidTanks;
private long timer = 0L;
private final int offset = GTValues.RNG.nextInt(20);
@@ -109,8 +106,7 @@ public boolean canAttachTo(Direction side) {
if (level.getBlockEntity(getBlockPos().relative(side)) instanceof FluidPipeBlockEntity) {
return false;
}
- return FluidTransferHelper.getFluidTransfer(level, getBlockPos().relative(side), side.getOpposite()) !=
- null;
+ return GTTransferUtils.hasAdjacentFluidHandler(level, getBlockPos(), side);
}
return false;
}
@@ -124,7 +120,7 @@ public LazyOptional getCapability(Capability capability, @Nullable Dir
if (tankList == null)
return LazyOptional.empty();
return ForgeCapabilities.FLUID_HANDLER.orEmpty(capability,
- LazyOptional.of(() -> FluidTransferHelperImpl.toFluidHandler(tankList)));
+ LazyOptional.of(() -> tankList));
}
} else if (capability == GTCapability.CAPABILITY_COVERABLE) {
return GTCapability.CAPABILITY_COVERABLE.orEmpty(capability, LazyOptional.of(this::getCoverContainer));
@@ -134,7 +130,7 @@ public LazyOptional getCapability(Capability capability, @Nullable Dir
return super.getCapability(capability, facing);
}
- public long getCapacityPerTank() {
+ public int getCapacityPerTank() {
return getNodeData().getThroughput() * 20;
}
@@ -150,12 +146,12 @@ public void update() {
int tanks = getNodeData().getChannels();
for (int i = 0, j = GTValues.RNG.nextInt(tanks); i < tanks; i++) {
int index = (i + j) % tanks;
- FluidStorage tank = getFluidTanks()[index];
+ CustomFluidTank tank = getFluidTanks()[index];
FluidStack fluid = tank.getFluid();
if (fluid.isEmpty() || fluid.getFluid() == Fluids.EMPTY)
continue;
if (fluid.getAmount() <= 0) {
- tank.setFluid(FluidStack.empty());
+ tank.setFluid(FluidStack.EMPTY);
continue;
}
@@ -168,10 +164,10 @@ public void update() {
}
}
- private void distributeFluid(int channel, FluidStorage tank, FluidStack fluid) {
+ private void distributeFluid(int channel, CustomFluidTank tank, FluidStack fluid) {
// Tank, From, Amount to receive
List tanks = new ArrayList<>();
- long amount = fluid.getAmount();
+ int amount = fluid.getAmount();
FluidStack maxFluid = fluid.copy();
double availableCapacity = 0;
@@ -187,17 +183,16 @@ private void distributeFluid(int channel, FluidStorage tank, FluidStack fluid) {
BlockEntity neighbor = getNeighbor(facing);
if (neighbor == null) continue;
- IFluidHandler handler = neighbor.getCapability(ForgeCapabilities.FLUID_HANDLER, facing.getOpposite())
+ IFluidHandler fluidHandler = neighbor.getCapability(ForgeCapabilities.FLUID_HANDLER, facing.getOpposite())
.resolve().orElse(null);
- IFluidTransfer fluidHandler = handler == null ? null : FluidTransferHelperImpl.toFluidTransfer(handler);
if (fluidHandler == null) continue;
- IFluidTransfer pipeTank = tank;
+ IFluidHandlerModifiable pipeTank = tank;
CoverBehavior cover = getCoverContainer().getCoverAtSide(facing);
// pipeTank should only be determined by the cover attached to the actual pipe
if (cover != null) {
- pipeTank = cover.getFluidTransferCap(pipeTank);
+ pipeTank = cover.getFluidHandlerCap(pipeTank);
// Shutter covers return null capability when active, so check here to prevent NPE
if (pipeTank == null || checkForPumpCover(cover)) continue;
} else {
@@ -209,12 +204,13 @@ private void distributeFluid(int channel, FluidStorage tank, FluidStack fluid) {
}
}
- FluidStack drainable = pipeTank.drain(maxFluid, true);
+ FluidStack drainable = pipeTank.drain(maxFluid, IFluidHandler.FluidAction.SIMULATE);
if (drainable.isEmpty() || drainable.getAmount() <= 0) {
continue;
}
- long filled = Math.min(fluidHandler.fill(maxFluid, true), drainable.getAmount());
+ int filled = Math.min(fluidHandler.fill(maxFluid, IFluidHandler.FluidAction.SIMULATE),
+ drainable.getAmount());
if (filled > 0) {
tanks.add(new FluidTransaction(fluidHandler, pipeTank, filled));
@@ -250,16 +246,16 @@ private void distributeFluid(int channel, FluidStorage tank, FluidStack fluid) {
if (toInsert.isEmpty() || toInsert.getFluid() == Fluids.EMPTY) continue;
toInsert.setAmount(transaction.amount);
- long inserted = transaction.target.fill(toInsert, false);
+ int inserted = transaction.target.fill(toInsert, IFluidHandler.FluidAction.EXECUTE);
if (inserted > 0) {
- transaction.pipeTank.drain(inserted, false);
+ transaction.pipeTank.drain(inserted, IFluidHandler.FluidAction.EXECUTE);
}
}
}
private boolean checkForPumpCover(@Nullable CoverBehavior cover) {
if (cover instanceof PumpCover coverPump) {
- long pipeThroughput = getNodeData().getThroughput() * 20;
+ int pipeThroughput = getNodeData().getThroughput() * 20;
if (coverPump.getCurrentMilliBucketsPerTick() > pipeThroughput) {
coverPump.setTransferRate(pipeThroughput);
}
@@ -272,11 +268,10 @@ public void checkAndDestroy(@NotNull FluidStack stack) {
Fluid fluid = stack.getFluid();
FluidPipeProperties prop = getNodeData();
- net.minecraftforge.fluids.FluidStack forgeStack = FluidHelperImpl.toFluidStack(stack);
- boolean burning = prop.getMaxFluidTemperature() < fluid.getFluidType().getTemperature(forgeStack);
- boolean leaking = !prop.isGasProof() && fluid.getFluidType().getDensity(forgeStack) < 0;
+ boolean burning = prop.getMaxFluidTemperature() < fluid.getFluidType().getTemperature(stack);
+ boolean leaking = !prop.isGasProof() && fluid.getFluidType().getDensity(stack) < 0;
boolean shattering = !prop.isCryoProof() &&
- fluid.getFluidType().getTemperature() < FluidConstants.CRYOGENIC_FLUID_THRESHOLD;
+ fluid.getFluidType().getTemperature(stack) < FluidConstants.CRYOGENIC_FLUID_THRESHOLD;
boolean corroding = false;
boolean melting = false;
@@ -325,7 +320,7 @@ public void destroyPipe(FluidStack stack, boolean isBurning, boolean isLeaking,
new AABB(getPipePos()).inflate(2));
for (LivingEntity entityLivingBase : entities) {
EntityDamageUtil.applyTemperatureDamage(entityLivingBase,
- stack.getFluid().getFluidType().getTemperature(FluidHelperImpl.toFluidStack(stack)),
+ stack.getFluid().getFluidType().getTemperature(stack),
2.0F, 10);
}
}
@@ -377,7 +372,7 @@ public void destroyPipe(FluidStack stack, boolean isBurning, boolean isLeaking,
new AABB(getPipePos()).inflate(2));
for (LivingEntity entityLivingBase : entities) {
EntityDamageUtil.applyTemperatureDamage(entityLivingBase,
- stack.getFluid().getFluidType().getTemperature(FluidHelperImpl.toFluidStack(stack)),
+ stack.getFluid().getFluidType().getTemperature(stack),
2.0F, 10);
}
}
@@ -402,7 +397,7 @@ public void destroyPipe(FluidStack stack, boolean isBurning, boolean isLeaking,
new AABB(getPipePos()).inflate(2));
for (LivingEntity entityLivingBase : entities) {
EntityDamageUtil.applyTemperatureDamage(entityLivingBase,
- stack.getFluid().getFluidType().getTemperature(FluidHelperImpl.toFluidStack(stack)),
+ stack.getFluid().getFluidType().getTemperature(stack),
2.0F, 10);
}
}
@@ -427,9 +422,9 @@ public FluidStack getContainedFluid(int channel) {
}
private void createTanksList() {
- fluidTanks = new FluidStorage[getNodeData().getChannels()];
+ fluidTanks = new CustomFluidTank[getNodeData().getChannels()];
for (int i = 0; i < getNodeData().getChannels(); i++) {
- fluidTanks[i] = new FluidStorage(getCapacityPerTank());
+ fluidTanks[i] = new CustomFluidTank(getCapacityPerTank());
}
pipeTankList = new PipeTankList(this, null, fluidTanks);
for (Direction facing : GTUtil.DIRECTIONS) {
@@ -451,7 +446,7 @@ public PipeTankList getTankList(Direction facing) {
return tankLists.getOrDefault(facing, pipeTankList);
}
- public FluidStorage[] getFluidTanks() {
+ public CustomFluidTank[] getFluidTanks() {
if (pipeTankList == null || fluidTanks == null) {
createTanksList();
}
@@ -476,7 +471,7 @@ public void saveCustomPersistedData(CompoundTag tag, boolean forDrop) {
if (stack1 == null || stack1.getAmount() <= 0)
fluidTag.putBoolean("isNull", true);
else
- stack1.saveToTag(fluidTag);
+ stack1.writeToNBT(fluidTag);
list.add(fluidTag);
}
tag.put("Fluids", list);
@@ -490,7 +485,7 @@ public void loadCustomPersistedData(CompoundTag nbt) {
for (int i = 0; i < list.size(); i++) {
CompoundTag tag = list.getCompound(i);
if (!tag.getBoolean("isNull")) {
- fluidTanks[i].setFluid(FluidStack.loadFromTag(tag));
+ fluidTanks[i].setFluid(FluidStack.loadFluidStackFromNBT(tag));
}
}
}
@@ -559,11 +554,11 @@ public static void setNeighboursToFire(Level world, BlockPos selfPos) {
private static class FluidTransaction {
- public final IFluidTransfer target;
- public final IFluidTransfer pipeTank;
- public long amount;
+ public final IFluidHandler target;
+ public final IFluidHandler pipeTank;
+ public int amount;
- private FluidTransaction(IFluidTransfer target, IFluidTransfer pipeTank, long amount) {
+ private FluidTransaction(IFluidHandler target, IFluidHandler pipeTank, int amount) {
this.target = target;
this.pipeTank = pipeTank;
this.amount = amount;
diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/FluidFilterCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/FluidFilterCover.java
index 9e15898822..bd575f9c85 100644
--- a/src/main/java/com/gregtechceu/gtceu/common/cover/FluidFilterCover.java
+++ b/src/main/java/com/gregtechceu/gtceu/common/cover/FluidFilterCover.java
@@ -5,17 +5,17 @@
import com.gregtechceu.gtceu.api.cover.CoverDefinition;
import com.gregtechceu.gtceu.api.cover.IUICover;
import com.gregtechceu.gtceu.api.cover.filter.FluidFilter;
-import com.gregtechceu.gtceu.api.transfer.fluid.FluidTransferDelegate;
+import com.gregtechceu.gtceu.api.transfer.fluid.FluidHandlerDelegate;
+import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable;
import com.lowdragmc.lowdraglib.gui.widget.LabelWidget;
import com.lowdragmc.lowdraglib.gui.widget.Widget;
import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup;
-import com.lowdragmc.lowdraglib.side.fluid.FluidStack;
-import com.lowdragmc.lowdraglib.side.fluid.FluidTransferHelper;
-import com.lowdragmc.lowdraglib.side.fluid.IFluidTransfer;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.core.Direction;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidUtil;
import org.jetbrains.annotations.Nullable;
@@ -31,7 +31,7 @@
public class FluidFilterCover extends CoverBehavior implements IUICover {
protected FluidFilter fluidFilter;
- private FilteredFluidTransferWrapper fluidFilterWrapper;
+ private FilteredFluidHandlerWrapper fluidFilterWrapper;
public FluidFilterCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide) {
super(definition, coverHolder, attachedSide);
@@ -39,7 +39,7 @@ public FluidFilterCover(CoverDefinition definition, ICoverable coverHolder, Dire
@Override
public boolean canAttach() {
- return FluidTransferHelper.getFluidTransfer(coverHolder.getLevel(), coverHolder.getPos(), attachedSide) != null;
+ return FluidUtil.getFluidHandler(coverHolder.getLevel(), coverHolder.getPos(), attachedSide).isPresent();
}
public FluidFilter getFluidFilter() {
@@ -50,13 +50,13 @@ public FluidFilter getFluidFilter() {
}
@Override
- public @Nullable IFluidTransfer getFluidTransferCap(@Nullable IFluidTransfer defaultValue) {
+ public @Nullable IFluidHandlerModifiable getFluidHandlerCap(@Nullable IFluidHandlerModifiable defaultValue) {
if (defaultValue == null) {
return null;
}
if (fluidFilterWrapper == null || fluidFilterWrapper.delegate != defaultValue) {
- this.fluidFilterWrapper = new FilteredFluidTransferWrapper(defaultValue);
+ this.fluidFilterWrapper = new FilteredFluidHandlerWrapper(defaultValue);
}
return fluidFilterWrapper;
@@ -70,24 +70,24 @@ public Widget createUIWidget() {
return group;
}
- private class FilteredFluidTransferWrapper extends FluidTransferDelegate {
+ private class FilteredFluidHandlerWrapper extends FluidHandlerDelegate {
- public FilteredFluidTransferWrapper(IFluidTransfer delegate) {
+ public FilteredFluidHandlerWrapper(IFluidHandlerModifiable delegate) {
super(delegate);
}
@Override
- public long fill(int tank, FluidStack resource, boolean simulate, boolean notifyChanges) {
+ public int fill(FluidStack resource, FluidAction action) {
if (!getFluidFilter().test(resource))
return 0;
- return super.fill(tank, resource, simulate, notifyChanges);
+ return super.fill(resource, action);
}
@Override
- public FluidStack drain(int tank, FluidStack resource, boolean simulate, boolean notifyChanges) {
+ public FluidStack drain(FluidStack resource, FluidAction action) {
if (!getFluidFilter().test(resource))
- return FluidStack.empty();
- return super.drain(tank, resource, simulate, notifyChanges);
+ return FluidStack.EMPTY;
+ return super.drain(resource, action);
}
}
}
diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/FluidRegulatorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/FluidRegulatorCover.java
index 15d8e249b0..744d952f01 100644
--- a/src/main/java/com/gregtechceu/gtceu/common/cover/FluidRegulatorCover.java
+++ b/src/main/java/com/gregtechceu/gtceu/common/cover/FluidRegulatorCover.java
@@ -5,19 +5,21 @@
import com.gregtechceu.gtceu.api.cover.filter.FluidFilter;
import com.gregtechceu.gtceu.api.cover.filter.SimpleFluidFilter;
import com.gregtechceu.gtceu.api.gui.widget.EnumSelectorWidget;
-import com.gregtechceu.gtceu.api.gui.widget.LongInputWidget;
+import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget;
import com.gregtechceu.gtceu.api.gui.widget.NumberInputWidget;
+import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable;
import com.gregtechceu.gtceu.common.cover.data.BucketMode;
import com.gregtechceu.gtceu.common.cover.data.TransferMode;
import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup;
-import com.lowdragmc.lowdraglib.side.fluid.FluidStack;
-import com.lowdragmc.lowdraglib.side.fluid.IFluidTransfer;
import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced;
import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted;
import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder;
import net.minecraft.core.Direction;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.capability.IFluidHandler;
+import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
@@ -26,7 +28,7 @@
public class FluidRegulatorCover extends PumpCover {
- private static final long MAX_STACK_SIZE = 2_048_000_000; // Capacity of quantum tank IX
+ private static final int MAX_STACK_SIZE = 2_048_000_000; // Capacity of quantum tank IX
@Persisted
@DescSynced
@@ -40,10 +42,10 @@ public class FluidRegulatorCover extends PumpCover {
@Persisted
@DescSynced
@Getter
- protected long globalTransferSizeMillibuckets;
- protected long fluidTransferBuffered = 0L;
+ protected int globalTransferSizeMillibuckets;
+ protected int fluidTransferBuffered = 0;
- private NumberInputWidget transferSizeInput;
+ private NumberInputWidget transferSizeInput;
private EnumSelectorWidget transferBucketModeInput;
public FluidRegulatorCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide, int tier) {
@@ -55,8 +57,8 @@ public FluidRegulatorCover(CoverDefinition definition, ICoverable coverHolder, D
//////////////////////////////////////
@Override
- protected long doTransferFluidsInternal(IFluidTransfer source, IFluidTransfer destination,
- long platformTransferLimit) {
+ protected int doTransferFluidsInternal(IFluidHandlerModifiable source, IFluidHandlerModifiable destination,
+ int platformTransferLimit) {
return switch (transferMode) {
case TRANSFER_ANY -> transferAny(source, destination, platformTransferLimit);
case TRANSFER_EXACT -> transferExact(source, destination, platformTransferLimit);
@@ -64,80 +66,82 @@ protected long doTransferFluidsInternal(IFluidTransfer source, IFluidTransfer de
};
}
- private long transferExact(IFluidTransfer source, IFluidTransfer destination, long platformTransferLimit) {
- long fluidLeftToTransfer = platformTransferLimit;
+ private int transferExact(IFluidHandler source, IFluidHandler destination, int platformTransferLimit) {
+ int fluidLeftToTransfer = platformTransferLimit;
for (int slot = 0; slot < source.getTanks(); slot++) {
- if (fluidLeftToTransfer <= 0L)
+ if (fluidLeftToTransfer <= 0)
break;
FluidStack sourceFluid = source.getFluidInTank(slot).copy();
- long supplyAmount = getFilteredFluidAmount(sourceFluid) * MILLIBUCKET_SIZE;
+ int supplyAmount = getFilteredFluidAmount(sourceFluid);
// If the remaining transferrable amount in this operation is not enough to transfer the full stack size,
// the remaining amount for this operation will be buffered and added to the next operation's maximum.
if (fluidLeftToTransfer + fluidTransferBuffered < supplyAmount) {
this.fluidTransferBuffered += fluidLeftToTransfer;
- fluidLeftToTransfer = 0L;
+ fluidLeftToTransfer = 0;
break;
}
- if (sourceFluid.isEmpty() || supplyAmount <= 0L)
+ if (sourceFluid.isEmpty() || supplyAmount <= 0)
continue;
sourceFluid.setAmount(supplyAmount);
- FluidStack drained = source.drain(sourceFluid, true);
+ FluidStack drained = source.drain(sourceFluid, FluidAction.SIMULATE);
if (drained.isEmpty() || drained.getAmount() < supplyAmount)
continue;
- long insertableAmount = destination.fill(drained.copy(), true);
+ int insertableAmount = destination.fill(drained.copy(), FluidAction.SIMULATE);
if (insertableAmount <= 0)
continue;
drained.setAmount(insertableAmount);
- drained = source.drain(drained, false);
+ drained = source.drain(drained, FluidAction.EXECUTE);
if (!drained.isEmpty()) {
- destination.fill(drained, false);
+ destination.fill(drained, FluidAction.EXECUTE);
fluidLeftToTransfer -= (drained.getAmount() - fluidTransferBuffered);
}
- fluidTransferBuffered = 0L;
+ fluidTransferBuffered = 0;
}
return platformTransferLimit - fluidLeftToTransfer;
}
- private long keepExact(IFluidTransfer source, IFluidTransfer destination, long platformTransferLimit) {
- long fluidLeftToTransfer = platformTransferLimit;
+ private int keepExact(IFluidHandlerModifiable source, IFluidHandlerModifiable destination,
+ int platformTransferLimit) {
+ int fluidLeftToTransfer = platformTransferLimit;
- final Map sourceAmounts = enumerateDistinctFluids(source, TransferDirection.EXTRACT);
- final Map destinationAmounts = enumerateDistinctFluids(destination, TransferDirection.INSERT);
+ final Map sourceAmounts = enumerateDistinctFluids(source, TransferDirection.EXTRACT);
+ final Map