getTransparentBuffers()
+ {
+ return this.transparentBuffers;
+ }
+
+ public void clearChunk()
+ {
+ this.opaqueBuffers.setTotalElementCount(0);
+ this.transparentBuffers.ifPresent(bufferSet -> bufferSet.setTotalElementCount(0));
+ }
+
+ public void setBounds(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
+ {
+ this.boundsMinX = minX;
+ this.boundsMinY = minY;
+ this.boundsMinZ = minZ;
+ this.boundsMaxX = maxX;
+ this.boundsMaxY = maxY;
+ this.boundsMaxZ = maxZ;
+ }
+
+ public void setHeights(float minHeight, float maxHeight)
+ {
+ this.minHeight = minHeight;
+ this.maxHeight = maxHeight;
+ }
+
+ public void resetLastGenTime()
+ {
+ this.ticksSinceLastGen = 0;
+ }
+
+ public int getTicksSinceLastGen()
+ {
+ return this.ticksSinceLastGen;
+ }
+
+ public float getBoundsMinX()
+ {
+ return this.boundsMinX;
+ }
+
+ public float getBoundsMinY()
+ {
+ return this.boundsMinY;
+ }
+
+ public float getBoundsMinZ()
+ {
+ return this.boundsMinZ;
+ }
+
+ public float getBoundsMaxX()
+ {
+ return this.boundsMaxX;
+ }
+
+ public float getBoundsMaxY()
+ {
+ return this.boundsMaxY;
+ }
+
+ public float getBoundsMaxZ()
+ {
+ return this.boundsMaxZ;
+ }
+
+ public float getMinHeight()
+ {
+ return this.minHeight;
+ }
+
+ public float getMaxHeight()
+ {
+ return this.maxHeight;
+ }
+
+ public float getAlpha(float partialTick)
+ {
+ return Mth.lerp(partialTick, this.alphaO, this.alpha);
+ }
+
+ public void destroy()
+ {
+ this.opaqueBuffers.destroy();
+ this.transparentBuffers.ifPresent(MeshChunk.BufferSet::destroy);
+ }
+
+ public static class BufferSet
+ {
+ private int bufferId = -1;
+ private @Nullable ByteBuffer buffer;
+ private int elementCount;
+ private final int bufferSize;
+ private final int maxElements;
+ private final int elementOffset;
+
+ public BufferSet(int maxElements, int elementOffset, int bytesPerElement)
+ {
+ this.bufferId = GL15.glGenBuffers();
+ this.maxElements = maxElements;
+ this.elementOffset = elementOffset;
+ this.bufferSize = maxElements * bytesPerElement;
+ this.buffer = MemoryTracker.create(this.bufferSize);
+ GL15.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, this.bufferId);
+ GL15.glBufferData(GL43.GL_SHADER_STORAGE_BUFFER, this.buffer, GL15.GL_DYNAMIC_DRAW);
+ GL15.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, 0);
+ }
+
+ public void setTotalElementCount(int count)
+ {
+ this.elementCount = count;
+ }
+
+ public int getElementCount()
+ {
+ return this.elementCount;
+ }
+
+ public int getMaxElements()
+ {
+ return this.maxElements;
+ }
+
+ public int getElementOffset()
+ {
+ return this.elementOffset;
+ }
+
+ public int getBufferSize()
+ {
+ return this.bufferSize;
+ }
+
+ public int getBufferId()
+ {
+ return this.bufferId;
+ }
+
+ public void destroy()
+ {
+ this.elementCount = 0;
+
+ if (this.bufferId >= 0)
+ {
+ RenderSystem.glDeleteBuffers(this.bufferId);
+ this.bufferId = -1;
+ }
+
+ if (this.buffer != null)
+ {
+ MemoryUtil.memFree(this.buffer);
+ this.buffer = null;
+ }
+ }
+ }
+}
diff --git a/dev/nonamecrackers2/simpleclouds/client/mesh/generator/CloudMeshGenerator.java b/dev/nonamecrackers2/simpleclouds/client/mesh/generator/CloudMeshGenerator.java
new file mode 100644
index 00000000..f2ae91fd
--- /dev/null
+++ b/dev/nonamecrackers2/simpleclouds/client/mesh/generator/CloudMeshGenerator.java
@@ -0,0 +1,1149 @@
+package dev.nonamecrackers2.simpleclouds.client.mesh.generator;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Queue;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.lwjgl.opengl.GL15;
+import org.lwjgl.opengl.GL31;
+import org.lwjgl.opengl.GL41;
+import org.lwjgl.opengl.GL42;
+import org.lwjgl.opengl.GL43;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Queues;
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.systems.RenderSystem;
+
+import dev.nonamecrackers2.simpleclouds.SimpleCloudsMod;
+import dev.nonamecrackers2.simpleclouds.client.mesh.LevelOfDetailOptions;
+import dev.nonamecrackers2.simpleclouds.client.mesh.RendererInitializeResult;
+import dev.nonamecrackers2.simpleclouds.client.mesh.chunk.MeshChunk;
+import dev.nonamecrackers2.simpleclouds.client.mesh.instancing.InstanceableMesh;
+import dev.nonamecrackers2.simpleclouds.client.mesh.lod.LevelOfDetailConfig;
+import dev.nonamecrackers2.simpleclouds.client.mesh.lod.PreparedChunk;
+import dev.nonamecrackers2.simpleclouds.client.shader.buffer.BindingManager;
+import dev.nonamecrackers2.simpleclouds.client.shader.buffer.ShaderStorageBufferObject;
+import dev.nonamecrackers2.simpleclouds.client.shader.compute.ComputeShader;
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudInfo;
+import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import dev.nonamecrackers2.simpleclouds.mixin.MixinFrustumAccessor;
+import net.minecraft.CrashReportCategory;
+import net.minecraft.client.renderer.culling.Frustum;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.packs.resources.ResourceManager;
+import net.minecraft.util.Mth;
+import net.minecraft.world.phys.AABB;
+
+/**
+ * Abstract mesh generator class that generates a cloud vertex mesh using computer shaders. Implementations are only available on the render thread.
+ *
+ * Use {@link CloudMeshGenerator#init} to initialize the mesh generator. This will initialize all needed buffers . Note
+ * that this is an expensive class and having multiple instances in one environment can cause GPU memory to run out quick (including
+ * available SSBO bindings).
+ *
+ * Use {@link CloudMeshGenerator#tick} each frame to generate the mesh at a fixed interval of frames
+ * (defined by {@link CloudMeshGenerator#setMeshGenInterval}) or use {@link CloudMeshGenerator#generateMesh} to generate
+ * it in a single call.
+ *
+ * Use {@link CloudMeshGenerator#render} to render the currently generated cloud mesh.
+ *
+ * @author nonamecrackers2
+ */
+public abstract class CloudMeshGenerator
+{
+ private static final Logger LOGGER = LogManager.getLogger("simpleclouds/CloudMeshGenerator");
+
+ public static final ResourceLocation MAIN_CUBE_MESH_GENERATOR = SimpleCloudsMod.id("cube_mesh");
+ public static final int MAX_NOISE_LAYERS = 4;
+ public static final int VERTICAL_CHUNK_SPAN = 8;
+ public static final int LOCAL_SIZE = 8;
+ public static final int WORK_SIZE = SimpleCloudsConstants.CHUNK_SIZE / LOCAL_SIZE;
+ public static final int TICKS_UNTIL_FADE_RESET = 120;
+
+ //Opaque
+ public static final int BYTES_PER_SIDE_INFO = 24;
+ public static final int MAX_SIDE_INFO_BUFFER_SIZE = 50331648;
+ public static final String SIDE_INFO_BUFFER_NAME = "SideInfoBuffer";
+ public static final String TOTAL_SIDES_NAME = "TotalSides";
+ public static final String SIDES_PER_CHUNK_NAME = "SidesPerChunk";
+ //Transparent
+ public static final int BYTES_PER_CUBE_INFO = 24;
+ public static final int MAX_TRANSPARENT_CUBE_INFO_BUFFER_SIZE = 50331648;
+ public static final String TRANSPARENT_CUBE_INFO_BUFFER_NAME = "TransparentCubeInfoBuffer";
+ public static final String TRANSPARENT_TOTAL_CUBES_NAME = "TotalTransparentCubes";
+ public static final String TRANSPARENT_CUBES_PER_CHUNK_NAME = "TransparentCubesPerChunk";
+
+ public static final String NOISE_LAYERS_NAME = "NoiseLayers";
+ public static final String LAYER_GROUPINGS_NAME = "LayerGroupings";
+
+ protected final ResourceLocation meshShaderLoc;
+ protected final int shaderType;
+ protected final boolean fadeNearOrigin;
+ protected final boolean shadedClouds;
+ protected final boolean useTransparency;
+ protected final LevelOfDetailConfig lodConfig;
+ protected final boolean useFixedMeshDataSectionSize;
+ protected @Nullable List chunks;
+ protected final List completedGenTasks = Lists.newArrayList();
+ protected final Queue chunkGenTasks = Queues.newArrayDeque();
+ protected final Supplier meshGenIntervalCalculator;
+ protected int meshGenInterval = 1;
+ protected int tasksPerTick;
+ protected @Nullable ComputeShader shader;
+
+ protected @Nullable InstanceableMesh sideMesh;
+ protected @Nullable InstanceableMesh cubeMesh;
+
+ // Left is for opaque geometry, right is for transparent
+ protected Pair meshGenStatus = Pair.of(CloudMeshGenerator.MeshGenStatus.NOT_INITIALIZED, CloudMeshGenerator.MeshGenStatus.NOT_INITIALIZED);
+ protected float scrollX;
+ protected float scrollY;
+ protected float scrollZ;
+ protected boolean testFacesFacingAway;
+ private float fadeStart;
+ private float fadeEnd;
+ private float cullDistance;
+ private int transparencyDistance;
+
+ private int opaqueBufferSize;
+ private int opaqueBufferBytesUsed;
+ private int transparentBufferSize;
+ private int transparentBufferBytesUsed;
+ private int opaqueBytesPerChunk;
+ private int transparentBytesPerChunk;
+
+ /**
+ * Creates a cloud mesh generator, but does not initialize it for generating (use {@link CloudMeshGenerator#init})
+ *
+ * @param meshShaderLoc
+ * The location of the cloud mesh generator compute shader
+ * @param lodConfig
+ * A level of detail configuration
+ * @param meshGenInterval
+ * The frame interval at which the generate the cloud mesh
+ */
+ public CloudMeshGenerator(ResourceLocation meshShaderLoc, int shaderType, boolean fadeNearOrigin, boolean shadedClouds, LevelOfDetailConfig lodConfig, Supplier meshGenIntervalCalculator, boolean useTransparency, boolean fixedMeshDataSectionSize)
+ {
+ this.meshShaderLoc = meshShaderLoc;
+ this.shaderType = shaderType;
+ this.fadeNearOrigin = fadeNearOrigin;
+ this.shadedClouds = shadedClouds;
+ this.useFixedMeshDataSectionSize = fixedMeshDataSectionSize;
+
+ this.lodConfig = lodConfig;
+ this.meshGenIntervalCalculator = meshGenIntervalCalculator;
+ this.useTransparency = useTransparency;
+
+ float maxRadius = this.getCloudAreaMaxRadius();
+ this.fadeStart = 0.9F * maxRadius;
+ this.fadeEnd = maxRadius;
+ this.transparencyDistance = (int)maxRadius / 2;
+ }
+
+ public boolean fadeNearOriginEnabled()
+ {
+ return this.fadeNearOrigin;
+ }
+
+ public boolean shadedCloudsEnabled()
+ {
+ return this.shadedClouds;
+ }
+
+ public boolean transparencyEnabled()
+ {
+ return this.useTransparency;
+ }
+
+ public boolean usesFixedMeshDataSectionSize()
+ {
+ return this.useFixedMeshDataSectionSize;
+ }
+
+ public LevelOfDetailConfig getLodConfig()
+ {
+ return this.lodConfig;
+ }
+
+ /**
+ * Specifies if faces not facing the camera should be tested during
+ * mesh generation on the GPU for whether they should be generated or not.
+ *
+ * Enabling can improve performance at the cost of some visual artifacts
+ * or an incomplete cloud mesh
+ *
+ * @param flag
+ * @return
+ */
+ public CloudMeshGenerator setTestFacesFacingAway(boolean flag)
+ {
+ this.testFacesFacingAway = flag;
+ return this;
+ }
+
+ /**
+ * Sets the fade start and end distances as decimal percentages
+ *
+ * @param fadeStart
+ * @param fadeEnd
+ */
+ public CloudMeshGenerator setFadeDistances(float fadeStart, float fadeEnd)
+ {
+ float fs = fadeStart;
+ float fe = fadeEnd;
+ if (fs > fe)
+ {
+ fs = fadeEnd;
+ fe = fadeStart;
+ }
+ this.fadeStart = fs * (float)this.getCloudAreaMaxRadius();
+ this.fadeEnd = fe * (float)this.getCloudAreaMaxRadius();
+ return this;
+ }
+
+ public CloudMeshGenerator setTransparencyRenderDistance(float percentage)
+ {
+ this.transparencyDistance = Mth.floor(percentage * (float)this.getCloudAreaMaxRadius());
+ return this;
+ }
+
+ public float getFadeStart()
+ {
+ return this.fadeStart;
+ }
+
+ public float getFadeEnd()
+ {
+ return this.fadeEnd;
+ }
+
+ public int getCloudAreaMaxRadius()
+ {
+ return this.lodConfig.getEffectiveChunkSpan() * WORK_SIZE * LOCAL_SIZE / 2;
+ }
+
+ public void setCullDistance(float dist)
+ {
+ if (dist <= 0.0F)
+ throw new IllegalArgumentException("Cull distance must be greater than zero");
+ this.cullDistance = dist;
+ }
+
+ public void disableCullDistance()
+ {
+ this.cullDistance = 0.0F;
+ }
+
+ public void setScroll(float x, float y, float z)
+ {
+ this.scrollX = x;
+ this.scrollY = y;
+ this.scrollZ = z;
+ }
+
+ public Pair getMeshGenStatus()
+ {
+ return this.meshGenStatus;
+ }
+
+ public @Nullable InstanceableMesh getSideMesh()
+ {
+ return this.sideMesh;
+ }
+
+ public @Nullable InstanceableMesh getCubeMesh()
+ {
+ return this.cubeMesh;
+ }
+
+ public int getOpaqueBufferSize()
+ {
+ return this.opaqueBufferSize;
+ }
+
+ public int getOpaqueBufferBytesUsed()
+ {
+ return this.opaqueBufferBytesUsed;
+ }
+
+ public int getTransparentBufferSize()
+ {
+ return this.transparentBufferSize;
+ }
+
+ public int getTransparentBufferBytesUsed()
+ {
+ return this.transparentBufferBytesUsed;
+ }
+
+ public int getOpaqueBytesPerChunk()
+ {
+ return this.opaqueBytesPerChunk;
+ }
+
+ public int getTransparentBytesPerChunk()
+ {
+ return this.transparentBytesPerChunk;
+ }
+
+ public int getTotalMeshChunks()
+ {
+ if (this.chunks == null)
+ return 0;
+ return this.chunks.size();
+ }
+
+ public int getMeshGenInterval()
+ {
+ return this.meshGenInterval;
+ }
+
+ public void close()
+ {
+ RenderSystem.assertOnRenderThreadOrInit();
+
+ this.opaqueBufferBytesUsed = 0;
+ this.opaqueBufferSize = 0;
+ this.opaqueBytesPerChunk = 0;
+ this.transparentBufferBytesUsed = 0;
+ this.transparentBufferSize = 0;
+ this.transparentBytesPerChunk = 0;
+
+ GL42.glMemoryBarrier(GL42.GL_ALL_BARRIER_BITS);
+ this.chunkGenTasks.clear();
+ this.completedGenTasks.clear();
+
+ if (this.shader != null)
+ this.shader.close();
+ this.shader = null;
+
+ if (this.chunks != null)
+ {
+ for (MeshChunk chunk : this.chunks)
+ chunk.destroy();
+ this.chunks = null;
+ }
+
+ if (this.sideMesh != null)
+ {
+ this.sideMesh.destroy();
+ this.sideMesh = null;
+ }
+
+ if (this.cubeMesh != null)
+ {
+ this.cubeMesh.destroy();
+ this.cubeMesh = null;
+ }
+ }
+
+ public boolean canRender()
+ {
+ return this.chunks != null;
+ }
+
+ public final RendererInitializeResult init(ResourceManager manager)
+ {
+ RendererInitializeResult.Builder builder = RendererInitializeResult.builder();
+
+ if (!RenderSystem.isOnRenderThreadOrInit())
+ return builder.errorUnknown(new IllegalStateException("Init not called on render thread"), "Mesh Generator; Head").build();
+
+ this.opaqueBufferBytesUsed = 0;
+ this.opaqueBufferSize = 0;
+ this.opaqueBytesPerChunk = 0;
+ this.transparentBufferBytesUsed = 0;
+ this.transparentBufferSize = 0;
+ this.transparentBytesPerChunk = 0;
+
+ GL42.glMemoryBarrier(GL42.GL_ALL_BARRIER_BITS);
+ this.chunkGenTasks.clear();
+ this.completedGenTasks.clear();
+
+ LOGGER.debug("Beginning mesh generator initialization");
+
+ if (this.shader != null)
+ {
+ LOGGER.debug("Freeing mesh compute shader");
+ this.shader.close();
+ this.shader = null;
+ }
+
+ if (this.chunks != null)
+ {
+ for (MeshChunk chunk : this.chunks)
+ chunk.destroy();
+ this.chunks = null;
+ }
+
+ try
+ {
+ LOGGER.debug("Creating mesh compute shader...");
+ this.shader = this.createShader(manager);
+ this.setupShader();
+ }
+ catch (IOException e)
+ {
+ //LOGGER.warn("Failed to load compute shader", e);
+ builder.errorCouldNotLoadMeshScript(e, "Mesh Generator; Compute Shader");
+ }
+ catch (Exception e)
+ {
+ builder.errorRecommendations(e, "Mesh Generator; Compute Shader");
+ }
+
+ try
+ {
+ this.initExtra(manager);
+ }
+ catch (Exception e)
+ {
+ builder.errorUnknown(e, "Init Extra");
+ }
+
+ List preparedChunks = this.getLodConfig().getPreparedChunks();
+ ImmutableList.Builder meshChunks = ImmutableList.builder();
+ int totalPreparedChunks = preparedChunks.size();
+ this.opaqueBytesPerChunk = Mth.ceil(this.opaqueBufferSize / totalPreparedChunks);
+ this.transparentBytesPerChunk = Mth.ceil(this.transparentBufferSize / totalPreparedChunks);
+ if (!this.useFixedMeshDataSectionSize)
+ {
+ this.opaqueBytesPerChunk *= 4;
+ this.transparentBytesPerChunk *= 4;
+ }
+ int maxOpaqueElements = Mth.floor((float)this.opaqueBytesPerChunk / (float)BYTES_PER_SIDE_INFO);
+ int maxTransparentElements = Mth.floor((float)this.transparentBytesPerChunk / (float)BYTES_PER_CUBE_INFO);
+ int opaqueElementOffset = 0;
+ int transparentElementOffset = 0;
+ for (PreparedChunk chunk : preparedChunks)
+ {
+ meshChunks.add(new MeshChunk(chunk, maxOpaqueElements, opaqueElementOffset, BYTES_PER_SIDE_INFO, maxTransparentElements, transparentElementOffset, BYTES_PER_CUBE_INFO, this.useTransparency));
+ opaqueElementOffset += maxOpaqueElements;
+ transparentElementOffset += maxTransparentElements;
+ }
+ this.chunks = meshChunks.build();
+
+ LOGGER.debug("Opaque buffer size: {} bytes, transparent buffer size: {} bytes", this.opaqueBufferSize, this.transparentBufferSize);
+
+ if (this.sideMesh != null)
+ this.sideMesh.destroy();
+ this.sideMesh = InstanceableMesh.defaultSide();
+
+ if (this.cubeMesh != null)
+ this.cubeMesh.destroy();
+ this.cubeMesh = InstanceableMesh.defaultCube();
+
+ BindingManager.printDebug();
+
+ LOGGER.debug("Finished initializing mesh generator");
+
+ return builder.build();
+ }
+
+ protected ComputeShader createShader(ResourceManager manager) throws IOException
+ {
+ ImmutableMap parameters = ImmutableMap.of(
+ "TYPE", String.valueOf(this.shaderType),
+ "FADE_NEAR_ORIGIN", this.fadeNearOrigin ? "1" : "0",
+ "STYLE", this.shadedClouds ? "1" : "0",
+ "TRANSPARENCY", this.useTransparency ? "1" : "0",
+ "FIXED_SECTION_SIZE", this.useFixedMeshDataSectionSize ? "1" : "0"
+ );
+ return ComputeShader.loadShader(this.meshShaderLoc, manager, LOCAL_SIZE, LOCAL_SIZE, LOCAL_SIZE, parameters);
+ }
+
+ protected void setupShader()
+ {
+ this.opaqueBufferSize = this.createBuffers(
+ TOTAL_SIDES_NAME,
+ SIDES_PER_CHUNK_NAME,
+ SIDE_INFO_BUFFER_NAME,
+ MAX_SIDE_INFO_BUFFER_SIZE * (this.useFixedMeshDataSectionSize ? 4 : 1)
+ );
+
+ if (this.useTransparency)
+ {
+ this.transparentBufferSize = this.createBuffers(
+ TRANSPARENT_TOTAL_CUBES_NAME,
+ TRANSPARENT_CUBES_PER_CHUNK_NAME,
+ TRANSPARENT_CUBE_INFO_BUFFER_NAME,
+ MAX_TRANSPARENT_CUBE_INFO_BUFFER_SIZE * (this.useFixedMeshDataSectionSize ? 4 : 1)
+ );
+ }
+
+ this.shader.forUniform("TotalLodLevels", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, this.lodConfig.getLods().length);
+ });
+
+ this.uploadFadeData();
+ }
+
+ private void uploadFadeData()
+ {
+ if (this.shader == null || !this.shader.isValid())
+ return;
+
+ this.shader.forUniform("TransparencyDistance", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, this.transparencyDistance);
+ });
+ this.shader.forUniform("FadeStart", (id, loc) -> {
+ GL41.glProgramUniform1f(id, loc, this.fadeStart);
+ });
+ this.shader.forUniform("FadeEnd", (id, loc) -> {
+ GL41.glProgramUniform1f(id, loc, this.fadeEnd);
+ });
+ }
+
+ private int createBuffers(String totalCounterName, String countPerChunkName, String elementInfoBufferName, int maxSize)
+ {
+ if (!this.useFixedMeshDataSectionSize)
+ {
+ ShaderStorageBufferObject totalCountBuffer = this.shader.createAndBindSSBO(totalCounterName, GL15.GL_DYNAMIC_COPY);
+ totalCountBuffer.allocateBuffer(4);
+ totalCountBuffer.writeData(b -> {
+ b.putInt(0, 0);
+ }, 4, false);
+ }
+
+ int bufferSize = this.shader.createAndBindSSBO(elementInfoBufferName, GL15.GL_DYNAMIC_COPY).allocateBuffer(maxSize);
+
+ int totalChunks = this.getLodConfig().getPreparedChunks().size();
+ int countPerChunkBufferSize = totalChunks * 4;
+ ShaderStorageBufferObject countPerChunkBuffer = this.shader.createAndBindSSBO(countPerChunkName, GL15.GL_DYNAMIC_COPY);
+ countPerChunkBuffer.allocateBuffer(countPerChunkBufferSize);
+ countPerChunkBuffer.writeData(b ->
+ {
+ for (int i = 0; i < totalChunks; i++)
+ b.putInt(0);
+ b.rewind();
+ }, countPerChunkBufferSize, false);
+
+ return bufferSize;
+ }
+
+ protected void initExtra(ResourceManager manager) throws IOException {}
+
+ /**
+ * Generates the entire cloud mesh at the origin at once
+ */
+ public void generateMesh()
+ {
+ RenderSystem.assertOnRenderThread();
+
+ if (this.shader == null || !this.shader.isValid())
+ return;
+
+ this.prepareMeshGen(0.0D, 0.0D, 0.0D, 0.0F, 0.0F, null, 1, 1.0F);
+
+ if (!this.chunkGenTasks.isEmpty())
+ this.doMeshGenning(this.chunkGenTasks.size());
+
+ this.meshGenStatus = this.finalizeMeshGen();
+ this.completedGenTasks.clear();
+ }
+
+ public void worldTick()
+ {
+ if (this.chunks != null)
+ this.chunks.forEach(MeshChunk::tick);
+ }
+
+ /**
+ * Generates the cloud mesh on a per-frame basis
+ *
+ * @param originX
+ * @param originY
+ * @param originZ
+ * @param frustum
+ */
+ public void genTick(double originX, double originY, double originZ, @Nullable Frustum frustum, float partialTick)
+ {
+ RenderSystem.assertOnRenderThread();
+
+ if (this.shader == null || !this.shader.isValid())
+ return;
+
+ float chunkSize = (float)SimpleCloudsConstants.CHUNK_SIZE;
+ float meshGenOffsetX = (float)Mth.floor(originX / chunkSize) * chunkSize;
+ float meshGenOffsetZ = (float)Mth.floor(originZ / chunkSize) * chunkSize;
+
+ if (this.chunkGenTasks.isEmpty()) //If we have no chunk gen tasks
+ {
+ this.meshGenStatus = this.finalizeMeshGen(); //Split the combined mesh data from the GPU, and store them in the VBOs for each chunk that was generated
+ this.completedGenTasks.clear(); //Clear the chunk gen tasks
+
+ //Prepare the next batch of chunks to generate meshes for
+ this.meshGenInterval = this.meshGenIntervalCalculator.get();
+ if (this.meshGenInterval <= 0)
+ throw new RuntimeException("Mesh gen interval is <= 0");
+ this.tasksPerTick = this.prepareMeshGen(originX, originY, originZ, meshGenOffsetX, meshGenOffsetZ, frustum, this.meshGenInterval, partialTick);
+ }
+ else
+ {
+ this.onOffGen();
+ }
+
+ //If there are mesh gen tasks, we do mesh genning
+ if (!this.chunkGenTasks.isEmpty())
+ this.doMeshGenning(this.tasksPerTick);
+ }
+
+ private static CloudMeshGenerator.MeshGenStatus fixedIterateAndCopyToChunkBuffer(int copyBufferId, int copyBufferSizeBytes, Collection chunks, Function byteOffsetPerChunk, Function chunkBufferId, Function bytesToCopyPerChunk, Function bufferSizeBytesPerChunk)
+ {
+ CloudMeshGenerator.MeshGenStatus result = CloudMeshGenerator.MeshGenStatus.NORMAL;
+
+ GlStateManager._glBindBuffer(GL31.GL_COPY_READ_BUFFER, copyBufferId);
+
+ for (MeshChunk chunk : chunks)
+ {
+ int bytesToCopy = bytesToCopyPerChunk.apply(chunk);
+ if (bytesToCopy > 0)
+ {
+ int maxSize = bufferSizeBytesPerChunk.apply(chunk);
+ if (bytesToCopy > maxSize) // Too many bytes to go in to the chunk mesh buffer
+ {
+ bytesToCopy = maxSize;
+ result = CloudMeshGenerator.MeshGenStatus.CHUNK_OVERFLOW;
+ }
+
+ int byteOffset = byteOffsetPerChunk.apply(chunk);
+ if (byteOffset + bytesToCopy > copyBufferSizeBytes) // TODO: Account for this overflow using mesh gen status
+ {
+ //TODO: Make sure this uses multiples of the size of a single element to avoid cutting off a single element
+ bytesToCopy = copyBufferSizeBytes - byteOffset;
+ if (bytesToCopy <= 0)
+ continue;
+ }
+
+ GlStateManager._glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, chunkBufferId.apply(chunk));
+ GL31.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, byteOffset, 0, bytesToCopy);
+ }
+ }
+
+ return result;
+ }
+
+ private static CloudMeshGenerator.MeshGenStatus packedIterateAndCopyToChunkBuffer(int copyBufferId, int copyBufferSizeBytes, Collection chunks, Function chunkBufferId, Function bytesToCopyPerChunk, Function bufferSizeBytesPerChunk)
+ {
+ CloudMeshGenerator.MeshGenStatus result = CloudMeshGenerator.MeshGenStatus.NORMAL;
+
+ GlStateManager._glBindBuffer(GL31.GL_COPY_READ_BUFFER, copyBufferId);
+
+ int currentBytes = 0;
+ for (MeshChunk chunk : chunks)
+ {
+ int totalBytes = bytesToCopyPerChunk.apply(chunk);
+ if (totalBytes > 0) //If the chunk has data that needs copying over
+ {
+ int lastBytesOffset = totalBytes;
+ int maxSize = bufferSizeBytesPerChunk.apply(chunk);
+ if (lastBytesOffset > maxSize) //Make sure we don't go over the maximum the chunk buffer can hold
+ {
+ lastBytesOffset = maxSize;
+ result = CloudMeshGenerator.MeshGenStatus.CHUNK_OVERFLOW;
+ }
+ boolean stop = false;
+ if (currentBytes + lastBytesOffset > copyBufferSizeBytes) //If the the byte offset will go over the size of the copy buffer, clamp
+ {
+ lastBytesOffset = copyBufferSizeBytes - currentBytes;
+ if (lastBytesOffset <= 0) // If it becomes negative however, we don't want to attempt to copy data over
+ return CloudMeshGenerator.MeshGenStatus.MESH_POOL_OVERFLOW;
+ stop = true; // After copying this data over we will stop, since there is no more space in the copy buffer to read data from
+ }
+
+ GlStateManager._glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, chunkBufferId.apply(chunk));
+ GL31.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, currentBytes, 0, lastBytesOffset);
+
+ currentBytes += totalBytes;
+
+ if (stop)
+ return CloudMeshGenerator.MeshGenStatus.MESH_POOL_OVERFLOW;
+ }
+ }
+
+ return result;
+ }
+
+ protected Pair finalizeMeshGen()
+ {
+ if (this.shader == null || !this.shader.isValid() || this.chunks == null)
+ return Pair.of(CloudMeshGenerator.MeshGenStatus.NOT_INITIALIZED, CloudMeshGenerator.MeshGenStatus.NOT_INITIALIZED);
+
+ if (this.completedGenTasks.isEmpty())
+ return Pair.of(CloudMeshGenerator.MeshGenStatus.NO_TASKS, CloudMeshGenerator.MeshGenStatus.NO_TASKS);
+
+ RenderSystem.assertOnRenderThread();
+
+ GL42.glMemoryBarrier(GL43.GL_SHADER_STORAGE_BARRIER_BIT);
+
+ CloudMeshGenerator.MeshGenStatus opaqueResult = CloudMeshGenerator.MeshGenStatus.NORMAL;
+ CloudMeshGenerator.MeshGenStatus transparentResult = CloudMeshGenerator.MeshGenStatus.NORMAL;
+
+ opaqueResult = this.copyMeshData(
+ TOTAL_SIDES_NAME,
+ SIDES_PER_CHUNK_NAME,
+ SIDE_INFO_BUFFER_NAME,
+ MeshChunk::getOpaqueBuffers,
+ BYTES_PER_SIDE_INFO,
+ this.opaqueBufferSize
+ );
+
+ if (this.useTransparency)
+ {
+ transparentResult = this.copyMeshData(
+ TRANSPARENT_TOTAL_CUBES_NAME,
+ TRANSPARENT_CUBES_PER_CHUNK_NAME,
+ TRANSPARENT_CUBE_INFO_BUFFER_NAME,
+ c -> c.getTransparentBuffers().get(),
+ BYTES_PER_CUBE_INFO,
+ this.transparentBufferSize
+ );
+ }
+
+ this.opaqueBufferBytesUsed = 0;
+ this.transparentBufferBytesUsed = 0;
+ for (MeshChunk chunk : this.chunks)
+ {
+ this.opaqueBufferBytesUsed += chunk.getOpaqueBuffers().getElementCount() * BYTES_PER_SIDE_INFO;
+ chunk.getTransparentBuffers().ifPresent(bufferSet -> {
+ this.transparentBufferBytesUsed += bufferSet.getElementCount() * BYTES_PER_CUBE_INFO;
+ });
+ }
+
+ return Pair.of(opaqueResult, transparentResult);
+ }
+
+ private CloudMeshGenerator.MeshGenStatus copyMeshData(String totalCountBufferName, String countPerChunkBufferName, String elementBufferName, Function bufferSetFunction, int bytesPerElement, int elementBufferSize)
+ {
+ CloudMeshGenerator.MeshGenStatus status = CloudMeshGenerator.MeshGenStatus.NORMAL;
+
+ if (!this.useFixedMeshDataSectionSize)
+ {
+ //Get the total amount of sides and indices across all chunks and reset
+ this.shader.getShaderStorageBuffer(totalCountBufferName).writeData(b -> {
+ b.putInt(0, 0);
+ }, 4, true);
+ }
+
+ //Get the amount of total sides each chunk has and reset each counter
+ this.shader.getShaderStorageBuffer(countPerChunkBufferName).readWriteData(buffer ->
+ {
+ for (CloudMeshGenerator.ChunkGenTask gennedChunk : this.completedGenTasks)
+ {
+ MeshChunk.BufferSet bufferSet = bufferSetFunction.apply(gennedChunk.chunk());
+ int index = gennedChunk.index() * 4;
+ int count = buffer.getInt(index);
+ bufferSet.setTotalElementCount(count);
+ buffer.putInt(index, 0);
+ }
+ }, this.chunks.size() * 4);
+
+ List completedChunks = this.completedGenTasks.stream().map(CloudMeshGenerator.ChunkGenTask::chunk).toList();
+
+ int elementBufferId = this.shader.getShaderStorageBuffer(elementBufferName).getId();
+ if (this.useFixedMeshDataSectionSize)
+ status = fixedIterateAndCopyToChunkBuffer(elementBufferId, elementBufferSize, completedChunks, bufferSetFunction.andThen(b -> b.getElementOffset() * bytesPerElement), bufferSetFunction.andThen(MeshChunk.BufferSet::getBufferId), bufferSetFunction.andThen(c -> c.getElementCount() * bytesPerElement), bufferSetFunction.andThen(MeshChunk.BufferSet::getBufferSize));
+ else
+ status = packedIterateAndCopyToChunkBuffer(elementBufferId, elementBufferSize, completedChunks, bufferSetFunction.andThen(MeshChunk.BufferSet::getBufferId), bufferSetFunction.andThen(c -> c.getElementCount() * bytesPerElement), bufferSetFunction.andThen(MeshChunk.BufferSet::getBufferSize));
+
+ GlStateManager._glBindBuffer(GL31.GL_COPY_READ_BUFFER, 0);
+ GlStateManager._glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, 0);
+
+ return status;
+ }
+
+ /**
+ * Queues a list of chunk gen tasks for each chunk in this mesh generator
+ *
+ * @param meshGenOffsetX
+ * @param meshGenOffsetZ
+ * @param frustum
+ * Culling frustum, null for no culling
+ * @param genInterval
+ * How many frames mesh genning should take
+ * @return
+ */
+ protected int prepareMeshGen(double originX, double originY, double originZ, float meshGenOffsetX, float meshGenOffsetZ, @Nullable Frustum frustum, int genInterval, float partialTick)
+ {
+ this.shader.forUniform("Scroll", (id, loc) -> {
+ GL41.glProgramUniform3f(id, loc, this.scrollX, this.scrollY, this.scrollZ);
+ });
+ this.shader.forUniform("Wiggle", (id, loc) -> {
+ GL41.glProgramUniform1f(id, loc, (this.scrollX + this.scrollY + this.scrollZ) / 5.0F);
+ });
+ this.shader.forUniform("Origin", (id, loc) -> {
+ GL41.glProgramUniform3f(id, loc, (float)originX, (float)originY, (float)originZ);
+ });
+ this.shader.forUniform("TestFacesFacingAway", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, this.testFacesFacingAway ? 1 : 0);
+ });
+ this.uploadFadeData();
+
+ int chunkCount = 0;
+ for (int i = 0; i < this.chunks.size(); i++)
+ {
+ if (this.queueChunkMeshGenTaskOrClear(this.chunks.get(i), i, meshGenOffsetX, meshGenOffsetZ, frustum))
+ chunkCount++;
+ }
+ return Mth.ceil((float)chunkCount / (float)genInterval);
+ }
+
+ protected void onOffGen()
+ {
+ //We read these SSBOs here to avoid weird frame spikes when in fullscreen V-Sync, not sure why it happens
+ if (!this.useFixedMeshDataSectionSize)
+ this.shader.getShaderStorageBuffer(TOTAL_SIDES_NAME).readWriteData(b -> {}, 4);
+ this.shader.getShaderStorageBuffer(SIDES_PER_CHUNK_NAME).readWriteData(buffer -> {}, this.chunks.size() * 4);
+ if (this.useTransparency)
+ {
+ if (!this.useFixedMeshDataSectionSize)
+ this.shader.getShaderStorageBuffer(TRANSPARENT_TOTAL_CUBES_NAME).readWriteData(b -> {}, 4);
+ this.shader.getShaderStorageBuffer(TRANSPARENT_CUBES_PER_CHUNK_NAME).readWriteData(buffer -> {}, this.chunks.size() * 4);
+ }
+ }
+
+ /**
+ * Queues a given chunk for mesh genning or clears it if empty
+ *
+ * @param chunk
+ * The given {@link MeshChunk} to generate a mesh for
+ * @param chunkIndex
+ * The index of the mesh chunk in {@code this.chunks}
+ * @param meshGenOffsetX
+ * @param meshGenOffsetZ
+ * @param frustum
+ * For frustum culling, null for no culling
+ * @return
+ */
+ protected boolean queueChunkMeshGenTaskOrClear(MeshChunk chunk, int chunkIndex, float meshGenOffsetX, float meshGenOffsetZ, @Nullable Frustum frustum)
+ {
+ PreparedChunk chunkInfo = chunk.getChunkInfo();
+ AABB bounds = chunkInfo.bounds();
+ float minX = (float)bounds.minX + meshGenOffsetX;
+ float minZ = (float)bounds.minZ + meshGenOffsetZ;
+ float maxX = (float)bounds.maxX + meshGenOffsetX;
+ float maxZ = (float)bounds.maxZ + meshGenOffsetZ;
+
+ if (frustum == null || ((MixinFrustumAccessor)frustum).simpleclouds$cubeInFrustum(minX, bounds.minY, minZ, maxX, bounds.maxY, maxZ))
+ {
+ double nearestCornerX = Math.max(Math.max(bounds.minX, -bounds.maxX), 0.0D);
+ double nearestCornerZ = Math.max(Math.max(bounds.minZ, -bounds.maxZ), 0.0D);
+ double dist = Math.sqrt(nearestCornerX * nearestCornerX + nearestCornerZ * nearestCornerZ);
+
+ if (this.cullDistance <= 0.0F || dist < this.cullDistance)
+ {
+ CloudMeshGenerator.ChunkGenSettings settings = this.determineChunkGenSettings(minX, minZ, maxX, maxZ);
+ if (settings.skipChunk())
+ {
+ chunk.clearChunk();
+ return false;
+ }
+ this.chunkGenTasks.add(new CloudMeshGenerator.ChunkGenTask(chunk, minX, (float)bounds.minY, minZ, maxX, (float)bounds.maxY, maxZ, chunkIndex, minX, 0.0F, minZ, settings.minimumHeight(), settings.maximumHeight()));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected abstract CloudMeshGenerator.ChunkGenSettings determineChunkGenSettings(float minX, float minZ, float maxX, float maxZ);
+
+ /**
+ * Does mesh generating for a given amount of chunks defined by tasksPerTick
+ *
+ * @param tasksPerTick
+ */
+ protected void doMeshGenning(int tasksPerTick)
+ {
+ for (int i = 0; i < tasksPerTick; i++)
+ {
+ CloudMeshGenerator.ChunkGenTask task = this.chunkGenTasks.poll();
+ if (task != null)
+ {
+ this.generateChunk(task);
+ this.updateMeshChunkAfterGeneration(task.chunk(), task);
+ this.completedGenTasks.add(task);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ protected void updateMeshChunkAfterGeneration(MeshChunk chunk, CloudMeshGenerator.ChunkGenTask task)
+ {
+ chunk.setBounds(task.minX(), task.minY(), task.minZ(), task.maxX(), task.maxY(), task.maxZ());
+ chunk.setHeights(task.startY(), task.endY());
+ chunk.resetLastGenTime();
+ }
+
+ /**
+ * Generates a given chunk, or completes a chunk gen task
+ *
+ * @param task
+ * @param scale
+ * @param globalOffsetX
+ * @param globalOffsetZ
+ */
+ protected void generateChunk(CloudMeshGenerator.ChunkGenTask task)
+ {
+ PreparedChunk chunkInfo = task.chunk().getChunkInfo();
+
+ int lodScale = chunkInfo.lodScale();
+ int lowestY = task.startY();
+ int height = Mth.ceil((float)(task.endY() - lowestY) / (float)lodScale);
+ int localHeightInvocations = Mth.ceil((float)height / (float)LOCAL_SIZE);
+
+ if (localHeightInvocations > 0)
+ {
+ this.shader.forUniform("ChunkIndex", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, task.index());
+ });
+ this.shader.forUniform("LodLevel", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, chunkInfo.lodLevel());
+ });
+ this.shader.forUniform("RenderOffset", (id, loc) -> {
+ GL41.glProgramUniform3f(id, loc, task.x(), task.y() + lowestY, task.z());
+ });
+ this.shader.forUniform("Scale", (id, loc) -> {
+ GL41.glProgramUniform1f(id, loc, lodScale);
+ });
+ this.shader.forUniform("DoNotOccludeSide", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, chunkInfo.noOcclusionDirectionIndex());
+ });
+ if (this.useFixedMeshDataSectionSize)
+ {
+ this.shader.forUniform("OpaqueMeshDataOffset", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, task.chunk().getOpaqueBuffers().getElementOffset());
+ });
+
+ task.chunk().getTransparentBuffers().ifPresent(bufferSet ->
+ {
+ this.shader.forUniform("TransparentMeshDataOffset", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, bufferSet.getElementOffset());
+ });
+ });
+ }
+
+ this.shader.dispatch(WORK_SIZE, localHeightInvocations, WORK_SIZE, false);
+ if (!this.useFixedMeshDataSectionSize)
+ GL42.glMemoryBarrier(GL43.GL_SHADER_STORAGE_BARRIER_BIT);
+ }
+ }
+
+ public void forRenderableMeshChunks(@Nullable Frustum frustum, Function bufferSetFunction, BiConsumer function)
+ {
+ this.forRenderableMeshChunks(frustum, bufferSetFunction, function, false);
+ }
+
+ public void forRenderableMeshChunks(@Nullable Frustum frustum, Function bufferSetFunction, BiConsumer function, boolean updateFade)
+ {
+ for (MeshChunk chunk : this.chunks)
+ {
+ MeshChunk.BufferSet bufferSet = bufferSetFunction.apply(chunk);
+ if (bufferSet.getElementCount() > 0)
+ {
+ if (updateFade && chunk.getTicksSinceLastGen() > TICKS_UNTIL_FADE_RESET)
+ {
+ chunk.resetAlpha();
+ chunk.setFadeEnabled(false);
+ }
+
+ boolean render = true;
+ if (frustum != null)
+ render = ((MixinFrustumAccessor)frustum).simpleclouds$cubeInFrustum(chunk.getBoundsMinX(), chunk.getBoundsMinY(), chunk.getBoundsMinZ(), chunk.getBoundsMaxX(), chunk.getBoundsMaxY(), chunk.getBoundsMaxZ());
+
+ if (render)
+ {
+ PreparedChunk chunkInfo = chunk.getChunkInfo();
+ AABB bounds = chunkInfo.bounds();
+ double nearestCornerX = Math.max(Math.max(bounds.minX, -bounds.maxX), 0.0D);
+ double nearestCornerZ = Math.max(Math.max(bounds.minZ, -bounds.maxZ), 0.0D);
+ double dist = Math.sqrt(nearestCornerX * nearestCornerX + nearestCornerZ * nearestCornerZ);
+ if (this.cullDistance <= 0.0F || this.cullDistance > dist)
+ {
+ if (updateFade)
+ chunk.setFadeEnabled(true);
+ function.accept(chunk, bufferSet);
+ }
+ }
+ }
+ }
+ }
+
+ public void fillReport(CrashReportCategory category)
+ {
+ category.setDetail("Shader Type", this.shaderType);
+ category.setDetail("Shaded Clouds", this.shadedClouds);
+ category.setDetail("Transparency Enabled", this.useTransparency);
+ category.setDetail("Fade Near Origin", this.fadeNearOrigin);
+ category.setDetail("Compute Shader", this.shader);
+ category.setDetail("Level Of Details", 1 + this.lodConfig.getLods().length);
+ category.setDetail("Generation Frame Interval", this.meshGenInterval);
+ category.setDetail("Total Prepared Chunks", this.lodConfig.getPreparedChunks().size());
+ category.setDetail("Tasks Per Frame", this.tasksPerTick);
+ category.setDetail("Scroll", String.format("X: %s, Y: %s, Z: %s", this.scrollX, this.scrollY, this.scrollZ));
+ category.setDetail("Total Mesh Chunks", this.chunks != null ? this.chunks.size() : "null");
+ category.setDetail("Mesh Gen Status", this.meshGenStatus);
+ category.setDetail("Test Occluded Faces", this.testFacesFacingAway);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s[shader_name=%s]", this.getClass().getSimpleName(), this.meshShaderLoc);
+ }
+
+ protected static CloudMeshGenerator.ChunkGenSettings skip()
+ {
+ return new CloudMeshGenerator.ChunkGenSettings(true, 0, 0);
+ }
+
+ protected static CloudMeshGenerator.ChunkGenSettings heights(int min, int max)
+ {
+ return new CloudMeshGenerator.ChunkGenSettings(false, min, max);
+ }
+
+ public static CloudMeshGenerator.Builder builder()
+ {
+ return new CloudMeshGenerator.Builder();
+ }
+
+ protected static record ChunkGenSettings(boolean skipChunk, int minimumHeight, int maximumHeight) {}
+
+ protected static record ChunkGenTask(MeshChunk chunk, float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int index, float x, float y, float z, int startY, int endY) {}
+
+ public static enum MeshGenStatus
+ {
+ NOT_INITIALIZED("Not initialized", true),
+ NO_TASKS("No tasks", false),
+ NORMAL("Normal", false),
+ MESH_POOL_OVERFLOW("Mesh pool overflow", true),
+ CHUNK_OVERFLOW("Chunk overflow", true);
+
+ private String name;
+ private boolean isErroneous;
+
+ private MeshGenStatus(String name, boolean isErroneous)
+ {
+ this.name = name;
+ this.isErroneous = isErroneous;
+ }
+
+ public String getName()
+ {
+ return this.name;
+ }
+
+ public boolean isErroneous()
+ {
+ return this.isErroneous;
+ }
+ }
+
+ public static class Builder
+ {
+ private boolean fadeNearOrigin;
+ private boolean shadedClouds = true;
+ private LevelOfDetailConfig lodConfig = LevelOfDetailOptions.HIGH.getConfig();
+ private Supplier meshGenIntervalCalculator = () -> 5;
+ private boolean useTransparency = true;
+ private boolean fixedMeshDataSectionSize;
+ private float fadeStart = 0.5F;
+ private float fadeEnd = 1.0F;
+ private boolean testFacesFacingAway = false;
+
+ private Builder() {}
+
+ public Builder fadeNearOrigin(boolean flag)
+ {
+ this.fadeNearOrigin = flag;
+ return this;
+ }
+
+ public Builder shadedClouds(boolean flag)
+ {
+ this.shadedClouds = flag;
+ return this;
+ }
+
+ public Builder meshGenInterval(int interval)
+ {
+ if (interval <= 0)
+ throw new IllegalArgumentException("Mesh gen interval must be greater than 0");
+ this.meshGenIntervalCalculator = () -> interval;
+ return this;
+ }
+
+ public Builder meshGenInterval(Supplier calculator)
+ {
+ this.meshGenIntervalCalculator = calculator;
+ return this;
+ }
+
+ public Builder lodConfig(LevelOfDetailConfig config)
+ {
+ this.lodConfig = config;
+ return this;
+ }
+
+ public Builder useTransparency(boolean flag)
+ {
+ this.useTransparency = flag;
+ return this;
+ }
+
+ public Builder fixedMeshDataSectionSize(boolean flag)
+ {
+ this.fixedMeshDataSectionSize = flag;
+ return this;
+ }
+
+ public Builder fadeStart(float fadeStart)
+ {
+ this.fadeStart = fadeStart;
+ return this;
+ }
+
+ public Builder fadeEnd(float fadeEnd)
+ {
+ this.fadeEnd = fadeEnd;
+ return this;
+ }
+
+ public Builder testFacesFacingAway(boolean flag)
+ {
+ this.testFacesFacingAway = flag;
+ return this;
+ }
+
+ private T applyExtraSettings(T generator)
+ {
+ generator.setFadeDistances(this.fadeStart, this.fadeEnd);
+ generator.setTestFacesFacingAway(this.testFacesFacingAway);
+ return generator;
+ }
+
+ public MultiRegionCloudMeshGenerator createMultiRegion()
+ {
+ return this.applyExtraSettings(new MultiRegionCloudMeshGenerator(this.fadeNearOrigin, this.shadedClouds, this.lodConfig, this.meshGenIntervalCalculator, this.useTransparency, this.fixedMeshDataSectionSize));
+ }
+
+ public SingleRegionCloudMeshGenerator createSingleRegion(CloudInfo type)
+ {
+ return this.applyExtraSettings(new SingleRegionCloudMeshGenerator(this.shadedClouds, this.lodConfig, this.meshGenIntervalCalculator, this.useTransparency, this.fixedMeshDataSectionSize, type));
+ }
+ }
+}
diff --git a/dev/nonamecrackers2/simpleclouds/client/mesh/generator/MultiRegionCloudMeshGenerator.java b/dev/nonamecrackers2/simpleclouds/client/mesh/generator/MultiRegionCloudMeshGenerator.java
new file mode 100644
index 00000000..8226742f
--- /dev/null
+++ b/dev/nonamecrackers2/simpleclouds/client/mesh/generator/MultiRegionCloudMeshGenerator.java
@@ -0,0 +1,404 @@
+package dev.nonamecrackers2.simpleclouds.client.mesh.generator;
+
+import java.io.IOException;
+import java.nio.IntBuffer;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.joml.Matrix2f;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL12;
+import org.lwjgl.opengl.GL15;
+import org.lwjgl.opengl.GL30;
+import org.lwjgl.opengl.GL41;
+import org.lwjgl.opengl.GL42;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.mojang.blaze3d.platform.TextureUtil;
+import com.mojang.blaze3d.systems.RenderSystem;
+
+import dev.nonamecrackers2.simpleclouds.SimpleCloudsMod;
+import dev.nonamecrackers2.simpleclouds.client.mesh.lod.LevelOfDetail;
+import dev.nonamecrackers2.simpleclouds.client.mesh.lod.LevelOfDetailConfig;
+import dev.nonamecrackers2.simpleclouds.client.mesh.lod.PreparedChunk;
+import dev.nonamecrackers2.simpleclouds.client.shader.buffer.BindingManager;
+import dev.nonamecrackers2.simpleclouds.client.shader.buffer.ShaderStorageBufferObject;
+import dev.nonamecrackers2.simpleclouds.client.shader.compute.ComputeShader;
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudInfo;
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudType;
+import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import dev.nonamecrackers2.simpleclouds.common.cloud.region.CloudGetter;
+import dev.nonamecrackers2.simpleclouds.common.noise.AbstractNoiseSettings;
+import dev.nonamecrackers2.simpleclouds.common.noise.NoiseSettings;
+import net.minecraft.CrashReportCategory;
+import net.minecraft.client.renderer.culling.Frustum;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.packs.resources.ResourceManager;
+
+public final class MultiRegionCloudMeshGenerator extends CloudMeshGenerator
+{
+ private static final Logger LOGGER = LogManager.getLogger("simpleclouds/MultiRegionCloudMeshGenerator");
+
+ private static final ResourceLocation REGION_GENERATOR_LOC = SimpleCloudsMod.id("cloud_regions");
+ private static final String LOD_SCALES_NAME = "LodScales";
+ private static final String CLOUD_REGIONS_NAME = "CloudRegions";
+ public static final int MAX_CLOUD_TYPES = 64;
+ public static final int MAX_CLOUD_FORMATIONS = 10;
+ private static final int BYTES_PER_REGION = 32;
+ private int requiredRegionTexSize;
+ private CloudGetter cloudGetter = CloudGetter.EMPTY;
+ private CloudInfo[] cachedTypes = new CloudInfo[0];
+ private @Nullable ComputeShader regionTextureGenerator;
+ private int cloudRegionTextureId = -1;
+ private int cloudRegionImageBinding = -1;
+ private boolean updateCloudTypes;
+ private int currentCloudFormationCount;
+
+ protected MultiRegionCloudMeshGenerator(boolean fadeNearOrigin, boolean shadedClouds, LevelOfDetailConfig lodConfig, Supplier meshGenIntervalCalculator, boolean useTransparency, boolean fixedMeshDataSectionSize)
+ {
+ super(CloudMeshGenerator.MAIN_CUBE_MESH_GENERATOR, 0, fadeNearOrigin, shadedClouds, lodConfig, meshGenIntervalCalculator, useTransparency, fixedMeshDataSectionSize);
+ }
+
+ public void setCloudGetter(CloudGetter getter)
+ {
+ this.cloudGetter = Objects.requireNonNull(getter, "Cloud getter cannot be null");
+ this.updateCloudTypes();
+ }
+
+ public int getCloudRegionTextureId()
+ {
+ return this.cloudRegionTextureId;
+ }
+
+ public void updateCloudTypes()
+ {
+ this.updateCloudTypes = true;
+ }
+
+ public int getTotalCloudTypes()
+ {
+ return this.cachedTypes.length;
+ }
+
+ public int getCloudFormationCount()
+ {
+ return this.currentCloudFormationCount;
+ }
+
+ @Override
+ protected void setupShader()
+ {
+ super.setupShader();
+
+ this.cachedTypes = new CloudInfo[0];
+ this.updateCloudTypes = false;
+
+ this.shader.createAndBindSSBO(NOISE_LAYERS_NAME, GL15.GL_STATIC_DRAW).allocateBuffer(AbstractNoiseSettings.Param.values().length * 4 * MAX_NOISE_LAYERS * MAX_CLOUD_TYPES);
+ this.shader.createAndBindSSBO(LAYER_GROUPINGS_NAME, GL15.GL_STATIC_DRAW).allocateBuffer(CloudInfo.BYTES_PER_TYPE * MAX_CLOUD_TYPES);
+
+ this.uploadCloudTypeData();
+ }
+
+ @Override
+ protected void initExtra(ResourceManager manager) throws IOException
+ {
+ // Cloud region texture generator compute shader
+ // This texture is a 2D array texture, with a texture for each level of detail.
+ // The red channel contains the index for a cloud type in the main mesh compute shader, and
+ // the green channel contains an "edge fade" value for smooth cloud region boundaries.
+ // When generating the cloud mesh, the main mesh compute shader samples this array texture
+ // depending on what LOD it is generating for to determine what cloud type to construct
+
+ // Create the compute shader
+
+ this.currentCloudFormationCount = 0;
+ this.requiredRegionTexSize = 0;
+
+ if (this.regionTextureGenerator != null)
+ this.regionTextureGenerator.close();
+
+ var params = ImmutableMap.of("EDGE_FADE_FACTOR", String.valueOf(SimpleCloudsConstants.REGION_EDGE_FADE_FACTOR));
+ this.regionTextureGenerator = ComputeShader.loadShader(REGION_GENERATOR_LOC, manager, 16, 16, this.lodConfig.getLods().length + 1, params);
+
+ ShaderStorageBufferObject lodScales = this.regionTextureGenerator.createAndBindSSBO(LOD_SCALES_NAME, GL15.GL_STATIC_READ);
+ int lodScalesSize = this.lodConfig.getLods().length * 4 + 4;
+ lodScales.allocateBuffer(lodScalesSize);
+ lodScales.writeData(b -> {
+ b.putFloat(1.0F); // Primary chunk scale
+ for (LevelOfDetail l : this.lodConfig.getLods())
+ b.putFloat((float)l.chunkScale());
+ b.rewind();
+ }, lodScalesSize, false);
+
+ // Data for the cloud regions in world
+ this.regionTextureGenerator.createAndBindSSBO(CLOUD_REGIONS_NAME, GL15.GL_STATIC_READ).allocateBuffer(MAX_CLOUD_FORMATIONS * BYTES_PER_REGION);
+
+ // Create the cloud region 2D array texture
+
+ // Here we calculate the maximum size we need for this array texture,
+ // ensuring each block in the mesh will have a value to read in this texture
+ // when doing mesh generation
+ int prevSpan = this.lodConfig.getPrimaryChunkSpan();
+ int prevScale = 1;
+ int largestSpan = prevSpan;
+ for (LevelOfDetail config : this.lodConfig.getLods())
+ {
+ int scale = config.chunkScale();
+ int div = scale / prevScale;
+ prevScale = scale;
+ prevSpan = prevSpan / div + config.spread() * 2;
+ if (prevSpan > largestSpan)
+ largestSpan = prevSpan;
+ }
+ this.requiredRegionTexSize = largestSpan * SimpleCloudsConstants.CHUNK_SIZE;
+
+ if (this.cloudRegionTextureId != -1)
+ {
+ TextureUtil.releaseTextureId(this.cloudRegionTextureId);
+ this.cloudRegionTextureId = -1;
+ }
+
+ this.cloudRegionTextureId = TextureUtil.generateTextureId();
+ GL11.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.cloudRegionTextureId);
+ GL11.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
+ GL11.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
+ GL11.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ GL11.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ GL12.glTexImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, GL30.GL_RG32F, this.requiredRegionTexSize, this.requiredRegionTexSize, this.lodConfig.getLods().length + 1, 0, GL30.GL_RG, GL11.GL_FLOAT, (IntBuffer)null);
+ GL11.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, 0);
+
+ // Assign an image unit to it so any shader can access it
+ if (this.cloudRegionImageBinding != -1)
+ BindingManager.freeImageUnit(this.cloudRegionImageBinding);
+ this.cloudRegionImageBinding = BindingManager.getAvailableImageUnit();
+ BindingManager.useImageUnit(this.cloudRegionImageBinding);
+ GL42.glBindImageTexture(this.cloudRegionImageBinding, this.cloudRegionTextureId, 0, true, 0, GL15.GL_WRITE_ONLY, GL30.GL_RG32F);
+ this.regionTextureGenerator.setImageUnit("regionTexture", this.cloudRegionImageBinding);
+
+ this.runRegionGenerator(0.0F, 0.0F, 1.0F);
+
+ // Update the main mesh shader to use this texture
+ this.shader.setSampler2DArray("RegionsSampler", this.cloudRegionTextureId, 0);
+ this.shader.forUniform("RegionsTexSize", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, this.requiredRegionTexSize);
+ });
+
+ LOGGER.debug("Created cloud region texture generator with size {}x{}x{}", this.requiredRegionTexSize, this.requiredRegionTexSize, this.lodConfig.getLods().length + 1);
+ }
+
+ @Override
+ protected CloudMeshGenerator.ChunkGenSettings determineChunkGenSettings(float minX, float minZ, float maxX, float maxZ)
+ {
+ float[][] positions = new float[][] { {minX, minZ}, {minX, maxZ}, {maxX, minZ}, {maxX, maxZ} };
+ int smallestStartHeight = 0;
+ int largestEndHeight = 0;
+ boolean empty = true;
+ for (int i = 0; i < positions.length; i++)
+ {
+ float[] pos = positions[i];
+ Pair typeAt = this.cloudGetter.getCloudTypeAtPosition(pos[0], pos[1]);
+ if (typeAt.getRight() < 1.0F)
+ empty = false;
+ NoiseSettings config = typeAt.getLeft().noiseConfig();
+ int startHeight = config.getStartHeight();
+ int endHeight = config.getEndHeight();
+ if (i == 0 || smallestStartHeight > startHeight)
+ smallestStartHeight = startHeight;
+ if (i == 0 || largestEndHeight < endHeight)
+ largestEndHeight = endHeight;
+ }
+ if (empty || smallestStartHeight == largestEndHeight)
+ return skip();
+ else
+ return heights(smallestStartHeight, largestEndHeight);
+ }
+
+ @Override
+ protected void generateChunk(CloudMeshGenerator.ChunkGenTask task)
+ {
+ this.shader.forUniform("RegionSampleOffset", (id, loc) ->
+ {
+ PreparedChunk chunk = task.chunk().getChunkInfo();
+ GL41.glProgramUniform2f(id, loc, chunk.x() * (float)SimpleCloudsConstants.CHUNK_SIZE + (float)this.requiredRegionTexSize / 2.0F, chunk.z() * (float)SimpleCloudsConstants.CHUNK_SIZE + (float)this.requiredRegionTexSize / 2.0F);
+ });
+ this.shader.setSampler2DArray("RegionsSampler", this.cloudRegionTextureId, 0);
+
+ super.generateChunk(task);
+ }
+
+ private void runRegionGenerator(float meshOffsetX, float meshOffsetZ, float partialTick)
+ {
+ if (this.regionTextureGenerator == null || !this.regionTextureGenerator.isValid())
+ return;
+
+ this.uploadCloudRegionData(partialTick);
+ this.regionTextureGenerator.forUniform("Offset", (id, loc) -> {
+ GL41.glProgramUniform2f(id, loc, meshOffsetX, meshOffsetZ);
+ });
+ this.regionTextureGenerator.dispatchAndWait(this.requiredRegionTexSize / 16, this.requiredRegionTexSize / 16, 1);
+ }
+
+ private void uploadCloudRegionData(float partialTick)
+ {
+ if (this.regionTextureGenerator == null || !this.regionTextureGenerator.isValid())
+ return;
+
+ // Converts the cloud regions into data we can then easily pack into
+ // the SSBO. This method also checks and excludes cloud regions that
+ // reference cloud types that are not set up in this mesh generator
+ // to avoid errors.
+ List regionData = this.cloudGetter.getClouds().stream().map(region ->
+ {
+ Matrix2f transform = region.createTransform(partialTick);
+ float[] data = new float[] {
+ region.getPosX(partialTick),
+ region.getPosZ(partialTick),
+ (float)ArrayUtils.indexOf(this.cachedTypes, this.cloudGetter.getCloudTypeForId(region.getCloudTypeId())),
+ region.getRadius(partialTick),
+ transform.m00,
+ transform.m01,
+ transform.m10,
+ transform.m11
+ };
+ return data;
+ }).filter(data -> data[2] >= 0.0F).toList();
+
+ int regionDataSize = regionData.size();
+ int count = Math.min(MAX_CLOUD_FORMATIONS, regionDataSize);
+ if (regionDataSize != this.currentCloudFormationCount)
+ {
+ if (regionDataSize > MAX_CLOUD_FORMATIONS && regionDataSize > this.currentCloudFormationCount)
+ LOGGER.warn("Cloud formations {}/{}. Maximum count has been exceeded; some cloud formations will be ignored. Please ensure cloud formation count does not exceed the maximum of {}.", regionData.size(), MAX_CLOUD_FORMATIONS, MAX_CLOUD_FORMATIONS);
+ this.currentCloudFormationCount = regionDataSize;
+ }
+
+ if (count > 0)
+ {
+ ShaderStorageBufferObject regionsBuffer = this.regionTextureGenerator.getShaderStorageBuffer("CloudRegions");
+ regionsBuffer.writeData(b ->
+ {
+ for (int i = 0; i < count; i++)
+ {
+ float[] data = regionData.get(i);
+ for (float f : data)
+ b.putFloat(f);
+ }
+ b.rewind();
+ }, count * BYTES_PER_REGION, false);
+ }
+
+ this.regionTextureGenerator.forUniform("TotalCloudRegions", (id, loc) -> {
+ GL41.glProgramUniform1i(id, loc, count);
+ });
+ }
+
+ private void uploadCloudTypeData()
+ {
+ RenderSystem.assertOnRenderThreadOrInit();
+
+ if (this.shader != null && this.shader.isValid())
+ {
+ var toCopy = this.cloudGetter.getIndexedCloudTypes();
+ if (toCopy.length > MAX_CLOUD_TYPES)
+ LOGGER.warn("Cloud type count exceeds the maximum. Not all cloud types will render.");
+ int copySize = Math.min(MAX_CLOUD_TYPES, toCopy.length);
+ this.cachedTypes = Arrays.copyOf(toCopy, copySize);
+
+ LOGGER.debug("Uploading cloud type noise data...");
+
+ this.shader.getShaderStorageBuffer(LAYER_GROUPINGS_NAME).writeData(b ->
+ {
+ int previousLayerIndex = 0;
+ for (int i = 0; i < this.cachedTypes.length; i++)
+ {
+ CloudInfo type = this.cachedTypes[i];
+ previousLayerIndex = type.packToBuffer(b, previousLayerIndex);
+ }
+ b.rewind();
+ }, CloudInfo.BYTES_PER_TYPE * this.cachedTypes.length, false);
+
+ this.shader.getShaderStorageBuffer(NOISE_LAYERS_NAME).writeData(b ->
+ {
+ for (int i = 0; i < this.cachedTypes.length; i++)
+ {
+ NoiseSettings settings = this.cachedTypes[i].noiseConfig();
+ float[] packed = settings.packForShader();
+ for (int j = 0; j < packed.length && j < AbstractNoiseSettings.Param.values().length * MAX_NOISE_LAYERS; j++)
+ b.putFloat(packed[j]);
+ }
+ b.rewind();
+ }, AbstractNoiseSettings.Param.values().length * 4 * MAX_NOISE_LAYERS * this.cachedTypes.length, false);
+ }
+ }
+
+ @Override
+ protected int prepareMeshGen(double originX, double originY, double originZ, float meshGenOffsetX, float meshGenOffsetZ, @Nullable Frustum frustum, int interval, float partialTick)
+ {
+ if (this.updateCloudTypes)
+ {
+ this.uploadCloudTypeData();
+ this.updateCloudTypes = false;
+ }
+
+ this.runRegionGenerator(meshGenOffsetX, meshGenOffsetZ, partialTick);
+
+ return super.prepareMeshGen(originX, originY, originZ, meshGenOffsetX, meshGenOffsetZ, frustum, interval, partialTick);
+ }
+
+ @Override
+ protected void onOffGen()
+ {
+ super.onOffGen();
+
+ if (this.regionTextureGenerator != null)
+ this.regionTextureGenerator.getShaderStorageBuffer("CloudRegions").readData(buf -> {}, BYTES_PER_REGION * MAX_CLOUD_FORMATIONS);
+ }
+
+ @Override
+ public void close()
+ {
+ super.close();
+
+ this.currentCloudFormationCount = 0;
+ this.requiredRegionTexSize = 0;
+ this.updateCloudTypes = false;
+ this.cloudGetter = CloudGetter.EMPTY;
+ this.cachedTypes = new CloudInfo[0];
+
+ if (this.regionTextureGenerator != null)
+ {
+ this.regionTextureGenerator.close();
+ this.regionTextureGenerator = null;
+ }
+
+ if (this.cloudRegionTextureId != -1)
+ {
+ TextureUtil.releaseTextureId(this.cloudRegionTextureId);
+ this.cloudRegionTextureId = -1;
+ }
+
+ if (this.cloudRegionImageBinding != -1)
+ {
+ BindingManager.freeImageUnit(this.cloudRegionImageBinding);
+ this.cloudRegionImageBinding = -1;
+ }
+ }
+
+ @Override
+ public void fillReport(CrashReportCategory category)
+ {
+ category.setDetail("Cloud Types", "(" + this.cachedTypes.length + ") " + Joiner.on(", ").join(this.cachedTypes));
+ category.setDetail("Cloud Regions", this.cloudGetter.getClouds().size());
+ category.setDetail("Cloud Formations", this.currentCloudFormationCount);
+ super.fillReport(category);
+ }
+}
diff --git a/dev/nonamecrackers2/simpleclouds/client/mesh/instancing/InstanceableMesh.java b/dev/nonamecrackers2/simpleclouds/client/mesh/instancing/InstanceableMesh.java
new file mode 100644
index 00000000..49a38c40
--- /dev/null
+++ b/dev/nonamecrackers2/simpleclouds/client/mesh/instancing/InstanceableMesh.java
@@ -0,0 +1,219 @@
+package dev.nonamecrackers2.simpleclouds.client.mesh.instancing;
+
+import java.nio.ByteBuffer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL15;
+import org.lwjgl.opengl.GL30;
+import org.lwjgl.opengl.GL31;
+import org.lwjgl.system.MemoryUtil;
+
+import com.mojang.blaze3d.platform.MemoryTracker;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.DefaultVertexFormat;
+import com.mojang.blaze3d.vertex.VertexFormat;
+
+public class InstanceableMesh
+{
+ private int arrayObjectId = -1;
+ private int vertexBufferId = -1;
+ private int indexBufferId = -1;
+ private @Nullable ByteBuffer vertexBuffer;
+ private @Nullable ByteBuffer indexBuffer;
+ private int totalIndices;
+
+ public InstanceableMesh(int vertexBufferSize, int indexBufferSize, VertexFormat format, Consumer vertexBufferGenerator, Function indexBufferGenerator)
+ {
+ RenderSystem.assertOnRenderThread();
+
+ this.arrayObjectId = GL30.glGenVertexArrays();
+ this.vertexBufferId = GL15.glGenBuffers();
+ this.indexBufferId = GL15.glGenBuffers();
+
+ GL30.glBindVertexArray(this.arrayObjectId);
+
+ GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vertexBufferId);
+ this.vertexBuffer = MemoryTracker.create(vertexBufferSize);
+ vertexBufferGenerator.accept(this.vertexBuffer);
+ GL15.glBufferData(GL15.GL_ARRAY_BUFFER, this.vertexBuffer, GL15.GL_STATIC_DRAW);
+ format.setupBufferState();
+ GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, this.indexBufferId);
+ this.indexBuffer = MemoryTracker.create(indexBufferSize);
+ this.totalIndices = indexBufferGenerator.apply(this.indexBuffer);
+ GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, this.indexBuffer, GL15.GL_STATIC_DRAW);
+
+ GL30.glBindVertexArray(0);
+ }
+
+ public static InstanceableMesh defaultSide()
+ {
+ return new InstanceableMesh(48, 24, DefaultVertexFormat.POSITION, buffer ->
+ {
+ buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F);
+ buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F);
+ buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F);
+ buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F);
+ buffer.rewind();
+ }, buffer ->
+ {
+ buffer.putInt(0);
+ buffer.putInt(1);
+ buffer.putInt(2);
+ buffer.putInt(0);
+ buffer.putInt(2);
+ buffer.putInt(3);
+ buffer.rewind();
+ return 6;
+ });
+ }
+
+ public static InstanceableMesh defaultNonCulledSide()
+ {
+ return new InstanceableMesh(48, 48, DefaultVertexFormat.POSITION, buffer ->
+ {
+ buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F);
+ buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F);
+ buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F);
+ buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F);
+ buffer.rewind();
+ }, buffer ->
+ {
+ buffer.putInt(0);
+ buffer.putInt(1);
+ buffer.putInt(2);
+ buffer.putInt(0);
+ buffer.putInt(2);
+ buffer.putInt(3);
+
+ buffer.putInt(2);
+ buffer.putInt(1);
+ buffer.putInt(0);
+ buffer.putInt(3);
+ buffer.putInt(2);
+ buffer.putInt(0);
+
+ buffer.rewind();
+ return 12;
+ });
+ }
+
+// public static PreparedMesh defaultCube()
+// {
+// return new PreparedMesh(576, 144, SimpleCloudsShaders.POSITION_NORMAL, buffer -> {
+// //-x
+// buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.put((byte)-1); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.put((byte)-1); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.put((byte)-1); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.put((byte)-1); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)0);
+// //+x
+// buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.put((byte)1); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.put((byte)1); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.put((byte)1); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.put((byte)1); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)0);
+// //-y
+// buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.put((byte)0); buffer.put((byte)-1); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.put((byte)0); buffer.put((byte)-1); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.put((byte)0); buffer.put((byte)-1); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.put((byte)0); buffer.put((byte)-1); buffer.put((byte)0); buffer.put((byte)0);
+// //+y
+// buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.put((byte)0); buffer.put((byte)1); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.put((byte)0); buffer.put((byte)1); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.put((byte)0); buffer.put((byte)1); buffer.put((byte)0); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.put((byte)0); buffer.put((byte)1); buffer.put((byte)0); buffer.put((byte)0);
+// //-z
+// buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)-1); buffer.put((byte)0);
+// buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)-1); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)-1); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)-1); buffer.put((byte)0);
+// //-z
+// buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)1); buffer.put((byte)0);
+// buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)1); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)1); buffer.put((byte)0);
+// buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.put((byte)0); buffer.put((byte)0); buffer.put((byte)1); buffer.put((byte)0);
+//
+// buffer.rewind();
+// }, buffer -> {
+// buffer.putInt(0); buffer.putInt(1); buffer.putInt(2); buffer.putInt(0); buffer.putInt(2); buffer.putInt(3); // -x
+// buffer.putInt(4); buffer.putInt(5); buffer.putInt(6); buffer.putInt(4); buffer.putInt(6); buffer.putInt(7); // +x
+// buffer.putInt(8); buffer.putInt(9); buffer.putInt(10); buffer.putInt(8); buffer.putInt(10); buffer.putInt(11); // -y
+// buffer.putInt(12); buffer.putInt(13); buffer.putInt(14); buffer.putInt(12); buffer.putInt(14); buffer.putInt(15); // +y
+// buffer.putInt(16); buffer.putInt(17); buffer.putInt(18); buffer.putInt(16); buffer.putInt(18); buffer.putInt(19); // -z
+// buffer.putInt(20); buffer.putInt(21); buffer.putInt(22); buffer.putInt(20); buffer.putInt(22); buffer.putInt(23); // +z
+// buffer.rewind();
+// return 36;
+// });
+// }
+
+ public static InstanceableMesh defaultCube()
+ {
+ return new InstanceableMesh(96, 144, DefaultVertexFormat.POSITION, buffer ->
+ {
+ buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F);
+ buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.putFloat(-1.0F);
+ buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F);
+ buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat(-1.0F);
+ buffer.putFloat( 1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F);
+ buffer.putFloat( 1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F);
+ buffer.putFloat(-1.0F); buffer.putFloat( 1.0F); buffer.putFloat( 1.0F);
+ buffer.putFloat(-1.0F); buffer.putFloat(-1.0F); buffer.putFloat( 1.0F);
+ buffer.rewind();
+ }, buffer ->
+ {
+ buffer.putInt(0); buffer.putInt(1); buffer.putInt(2); buffer.putInt(0); buffer.putInt(2); buffer.putInt(3); // -z
+ buffer.putInt(4); buffer.putInt(7); buffer.putInt(6); buffer.putInt(4); buffer.putInt(6); buffer.putInt(5); // +z
+ buffer.putInt(7); buffer.putInt(0); buffer.putInt(3); buffer.putInt(7); buffer.putInt(3); buffer.putInt(6); // -x
+ buffer.putInt(1); buffer.putInt(4); buffer.putInt(5); buffer.putInt(1); buffer.putInt(5); buffer.putInt(2); // +x
+ buffer.putInt(1); buffer.putInt(0); buffer.putInt(7); buffer.putInt(1); buffer.putInt(7); buffer.putInt(4); // -y
+ buffer.putInt(5); buffer.putInt(6); buffer.putInt(3); buffer.putInt(5); buffer.putInt(3); buffer.putInt(2); // +y
+ buffer.rewind();
+ return 36;
+ });
+ }
+
+ public void drawInstanced(int count)
+ {
+ RenderSystem.assertOnRenderThread();
+
+ GL30.glBindVertexArray(this.arrayObjectId);
+ GL31.glDrawElementsInstanced(GL11.GL_TRIANGLES, this.totalIndices, GL11.GL_UNSIGNED_INT, 0L, count);
+ }
+
+ public void destroy()
+ {
+ this.totalIndices = 0;
+
+ if (this.arrayObjectId >= 0)
+ {
+ RenderSystem.glDeleteVertexArrays(this.arrayObjectId);
+ this.arrayObjectId = -1;
+ }
+
+ if (this.vertexBufferId >= 0)
+ {
+ RenderSystem.glDeleteBuffers(this.vertexBufferId);
+ this.vertexBufferId = -1;
+ }
+
+ if (this.vertexBuffer != null)
+ {
+ MemoryUtil.memFree(this.vertexBuffer);
+ this.vertexBuffer = null;
+ }
+
+ if (this.indexBufferId >= 0)
+ {
+ RenderSystem.glDeleteBuffers(this.indexBufferId);
+ this.indexBufferId = -1;
+ }
+
+ if (this.indexBuffer != null)
+ {
+ MemoryUtil.memFree(this.indexBuffer);
+ this.indexBuffer = null;
+ }
+ }
+}
diff --git a/dev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer.java b/dev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer.java
new file mode 100644
index 00000000..7bd37397
--- /dev/null
+++ b/dev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer.java
@@ -0,0 +1,1497 @@
+package dev.nonamecrackers2.simpleclouds.client.renderer;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+import javax.annotation.Nullable;
+
+import org.apache.commons.lang3.mutable.MutableInt;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.joml.Matrix4f;
+import org.joml.Vector2f;
+import org.joml.Vector3f;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL14;
+import org.lwjgl.opengl.GL15;
+import org.lwjgl.opengl.GL30;
+import org.lwjgl.opengl.GL40;
+import org.lwjgl.opengl.GL43;
+
+import com.google.common.collect.Lists;
+import com.google.gson.JsonSyntaxException;
+import com.mojang.blaze3d.pipeline.RenderTarget;
+import com.mojang.blaze3d.pipeline.TextureTarget;
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.platform.Window;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferBuilder;
+import com.mojang.blaze3d.vertex.BufferUploader;
+import com.mojang.blaze3d.vertex.DefaultVertexFormat;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.Tesselator;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+import com.mojang.blaze3d.vertex.VertexFormat;
+import com.mojang.math.Axis;
+
+import dev.nonamecrackers2.simpleclouds.SimpleCloudsMod;
+import dev.nonamecrackers2.simpleclouds.api.client.event.ModifyCloudRenderDistanceEvent;
+import dev.nonamecrackers2.simpleclouds.api.common.cloud.CloudMode;
+import dev.nonamecrackers2.simpleclouds.client.cloud.ClientSideCloudTypeManager;
+import dev.nonamecrackers2.simpleclouds.client.compat.SimpleCloudsCompatHelper;
+import dev.nonamecrackers2.simpleclouds.client.event.impl.DetermineCloudRenderPipelineEvent;
+import dev.nonamecrackers2.simpleclouds.client.framebuffer.CloudRenderTarget;
+import dev.nonamecrackers2.simpleclouds.client.framebuffer.ShadowMapBuffer;
+import dev.nonamecrackers2.simpleclouds.client.framebuffer.WeightedBlendingTarget;
+import dev.nonamecrackers2.simpleclouds.client.mesh.RendererInitializeResult;
+import dev.nonamecrackers2.simpleclouds.client.mesh.chunk.MeshChunk;
+import dev.nonamecrackers2.simpleclouds.client.mesh.generator.CloudMeshGenerator;
+import dev.nonamecrackers2.simpleclouds.client.mesh.generator.MultiRegionCloudMeshGenerator;
+import dev.nonamecrackers2.simpleclouds.client.mesh.generator.SingleRegionCloudMeshGenerator;
+import dev.nonamecrackers2.simpleclouds.client.mesh.lod.LevelOfDetailConfig;
+import dev.nonamecrackers2.simpleclouds.client.mesh.lod.PreparedChunk;
+import dev.nonamecrackers2.simpleclouds.client.renderer.lightning.LightningBolt;
+import dev.nonamecrackers2.simpleclouds.client.renderer.pipeline.CloudsRenderPipeline;
+import dev.nonamecrackers2.simpleclouds.client.renderer.settings.CloudsRendererSettings;
+import dev.nonamecrackers2.simpleclouds.client.shader.SimpleCloudsShaders;
+import dev.nonamecrackers2.simpleclouds.client.shader.SingleSSBOShaderInstance;
+import dev.nonamecrackers2.simpleclouds.client.shader.buffer.BindingManager;
+import dev.nonamecrackers2.simpleclouds.client.shader.buffer.ShaderStorageBufferObject;
+import dev.nonamecrackers2.simpleclouds.client.world.ClientCloudManager;
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudType;
+import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import dev.nonamecrackers2.simpleclouds.common.cloud.region.CloudGetter;
+import dev.nonamecrackers2.simpleclouds.common.config.SimpleCloudsConfig;
+import dev.nonamecrackers2.simpleclouds.mixin.MixinPostChain;
+import net.minecraft.CrashReport;
+import net.minecraft.CrashReportCategory;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.renderer.EffectInstance;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.client.renderer.LevelRenderer;
+import net.minecraft.client.renderer.LightTexture;
+import net.minecraft.client.renderer.PostChain;
+import net.minecraft.client.renderer.PostPass;
+import net.minecraft.client.renderer.ShaderInstance;
+import net.minecraft.client.renderer.culling.Frustum;
+import net.minecraft.client.renderer.texture.AbstractTexture;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.packs.resources.ResourceManager;
+import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
+import net.minecraft.util.FastColor;
+import net.minecraft.util.Mth;
+import net.minecraft.util.profiling.ProfilerFiller;
+import net.minecraft.world.effect.MobEffectInstance;
+import net.minecraft.world.effect.MobEffects;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.phys.Vec3;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.StartupMessageManager;
+import net.minecraftforge.fml.loading.ImmediateWindowHandler;
+import nonamecrackers2.crackerslib.common.compat.CompatHelper;
+
+public class SimpleCloudsRenderer implements ResourceManagerReloadListener
+{
+ private static final Logger LOGGER = LogManager.getLogger("simpleclouds/SimpleCloudsRenderer");
+ private static final Vector3f DIFFUSE_LIGHT_0 = (new Vector3f(0.2F, 1.0F, -0.7F)).normalize();
+ private static final Vector3f DIFFUSE_LIGHT_1 = (new Vector3f(-0.2F, 1.0F, 0.7F)).normalize();
+ private static final ResourceLocation STORM_POST_PROCESSING_LOC = SimpleCloudsMod.id("shaders/post/storm_post.json");
+ private static final ResourceLocation BLUR_POST_PROCESSING_LOC = SimpleCloudsMod.id("shaders/post/blur_post.json");
+ private static final ResourceLocation SCREEN_SPACE_WORLD_FOG_LOC = SimpleCloudsMod.id("shaders/post/screen_space_world_fog.json");
+ private static final ResourceLocation CLOUD_SHADOWS_LOC = SimpleCloudsMod.id("shaders/post/cloud_shadows.json");
+ public static final ResourceLocation FINAL_COMPOSITE_LOC = SimpleCloudsMod.id("shaders/post/final_composite.json");
+ public static final ResourceLocation FINAL_COMPOSITE_NO_TRANSPARENCY_LOC = SimpleCloudsMod.id("shaders/post/final_composite_no_transparency.json");
+ private static final ResourceLocation DITHER_TEXTURE = SimpleCloudsMod.id("textures/shader/bayer_matrix.png");
+ private static final ArtifactVersion REQUIRED_OPENGL_VERSION = new DefaultArtifactVersion("4.3");
+ public static final int SHADOW_MAP_SIZE = 1024;
+ public static final int SHADOW_MAP_SPAN = 10000;
+ public static final int MAX_LIGHTNING_BOLTS = 16;
+ public static final int BYTES_PER_LIGHTNING_BOLT = 16;
+ public static final float CHUNK_FADE_IN_ALPHA_PER_TICK = 0.2F;
+ public static final float DITHER_SCALE = 0.05F;
+ private static @Nullable SimpleCloudsRenderer instance;
+ private final CloudsRendererSettings settings;
+ private final Minecraft mc;
+ private final WorldEffects worldEffectsManager;
+ private final AtmosphericCloudsRenderHandler atmoshpericClouds;
+ private @Nullable ClientCloudManager cloudManager;
+ private ArtifactVersion openGlVersion;
+ private CloudMeshGenerator meshGenerator;
+ private @Nullable CloudsRenderPipeline renderPipelineThisPass;
+ private @Nullable RenderTarget cloudTarget;
+ private @Nullable WeightedBlendingTarget cloudTransparencyTarget;
+ private @Nullable RenderTarget stormFogTarget;
+ private int stormFogResolutionDivisor = 4;
+ private @Nullable RenderTarget blurTarget;
+ private final List postChains = Lists.newArrayList();
+ private @Nullable PostChain finalComposite;
+ private @Nullable PostChain stormPostProcessing;
+ private @Nullable PostChain blurPostProcessing;
+ private @Nullable PostChain screenSpaceWorldFog;
+ private @Nullable PostChain cloudShadows;
+ private @Nullable ShaderStorageBufferObject lightningBoltPositions;
+ private @Nullable ShadowMapBuffer stormFogShadowMap;
+ private Optional shadowMap = Optional.empty();
+ private @Nullable Frustum cullFrustum;
+ private float fogStart;
+ private float fogEnd;
+ private @Nullable PoseStack stormFogShadowMapStack;
+ private @Nullable PoseStack shadowMapStack;
+ private boolean failedToCopyDepthBuffer;
+ private boolean needsReload;
+ private @Nullable RendererInitializeResult initialInitializationResult;
+
+ private SimpleCloudsRenderer(CloudsRendererSettings settings, Minecraft mc)
+ {
+ this.settings = settings;
+ this.mc = mc;
+ this.worldEffectsManager = new WorldEffects(mc, this);
+ this.atmoshpericClouds = new AtmosphericCloudsRenderHandler(mc);
+ }
+
+ public String getClientCloudManagerString()
+ {
+ return this.cloudManager != null ? this.cloudManager.toString() : "null";
+ }
+
+ public CloudMeshGenerator getMeshGenerator()
+ {
+ return this.meshGenerator;
+ }
+
+ public CloudsRenderPipeline getRenderPipeline()
+ {
+ return Objects.requireNonNull(this.renderPipelineThisPass, "Pipeline not determined");
+ }
+
+ public WorldEffects getWorldEffectsManager()
+ {
+ return this.worldEffectsManager;
+ }
+
+ public AtmosphericCloudsRenderHandler getAtmosphericCloudRenderer()
+ {
+ return this.atmoshpericClouds;
+ }
+
+ public CloudsRendererSettings getSettings()
+ {
+ return this.settings;
+ }
+
+ public @Nullable RendererInitializeResult getInitialInitializationResult()
+ {
+ return this.initialInitializationResult;
+ }
+
+ public ShadowMapBuffer getStormFogShadowMap()
+ {
+ return this.stormFogShadowMap;
+ }
+
+ public Optional getShadowMap()
+ {
+ return this.shadowMap;
+ }
+
+ public @Nullable PoseStack getStormFogShadowMapStack()
+ {
+ return this.stormFogShadowMapStack;
+ }
+
+ public @Nullable PoseStack getShadowMapStack()
+ {
+ return this.shadowMapStack;
+ }
+
+ public RenderTarget getBlurTarget()
+ {
+ return this.blurTarget;
+ }
+
+ public RenderTarget getStormFogTarget()
+ {
+ return this.stormFogTarget;
+ }
+
+ public RenderTarget getCloudTarget()
+ {
+ return this.cloudTarget;
+ }
+
+ public WeightedBlendingTarget getCloudTransparencyTarget()
+ {
+ return this.cloudTransparencyTarget;
+ }
+
+ public float getFogStart()
+ {
+ return this.fogStart;
+ }
+
+ public float getFogEnd()
+ {
+ return this.fogEnd;
+ }
+
+ public float getFadeFactorForDistance(float distance)
+ {
+ return 1.0F - Math.min(Math.max(distance - this.fogStart, 0.0F) / (this.fogEnd - this.fogStart), 1.0F);
+ }
+
+ public @Nullable Frustum getCullFrustum()
+ {
+ return this.cullFrustum;
+ }
+
+ public void onCloudManagerChange(ClientCloudManager manager)
+ {
+ this.cloudManager = manager;
+ if (this.meshGenerator instanceof MultiRegionCloudMeshGenerator generator)
+ generator.setCloudGetter(manager);
+ }
+
+ private void prepareMeshGenerator(float partialTicks)
+ {
+ if (this.meshGenerator instanceof SingleRegionCloudMeshGenerator generator)
+ generator.setFadeDistances((float)SimpleCloudsConfig.CLIENT.singleModeFadeStartPercentage.get() / 100.0F, (float)SimpleCloudsConfig.CLIENT.singleModeFadeEndPercentage.get() / 100.0F);
+ this.meshGenerator.setTransparencyRenderDistance((float)SimpleCloudsConfig.CLIENT.transparencyRenderDistancePercentage.get() / 100.0F);
+ this.meshGenerator.setTestFacesFacingAway(SimpleCloudsConfig.CLIENT.testSidesThatAreOccluded.get());
+ if (this.mc.level != null)
+ {
+ this.meshGenerator.setScroll(this.cloudManager.getScrollX(partialTicks), this.cloudManager.getScrollY(partialTicks), this.cloudManager.getScrollZ(partialTicks));
+ }
+ }
+
+ public boolean needsReinitialization()
+ {
+ return this.settings.needsReinitialization(this.meshGenerator);
+ }
+
+ public void requestReload()
+ {
+ LOGGER.debug("Requesting reload...");
+ this.needsReload = true;
+ }
+
+ @Override
+ public void onResourceManagerReload(ResourceManager manager)
+ {
+ RenderSystem.assertOnRenderThreadOrInit();
+
+ this.initialInitializationResult = null;
+
+ // --- Check OpenGL version ---
+
+ ArtifactVersion openGlVersion = this.openGlVersion;
+ if (openGlVersion == null)
+ openGlVersion = new DefaultArtifactVersion(ImmediateWindowHandler.getGLVersion());
+ if (openGlVersion.compareTo(REQUIRED_OPENGL_VERSION) < 0)
+ {
+ LOGGER.error("Simple Clouds renderer could not initialize. OpenGL version is {}, minimum required is {}", openGlVersion, REQUIRED_OPENGL_VERSION);
+ this.initialInitializationResult = RendererInitializeResult.builder().errorOpenGL().build();
+ this.openGlVersion = openGlVersion;
+ return;
+ }
+
+ if (!SimpleCloudsShaders.areShadersInitialized())
+ {
+ LOGGER.error("Simple Clouds renderer could not initialize. Core shaders are not initialized.");
+ this.initialInitializationResult = RendererInitializeResult.builder().coreShadersNotInitialized(SimpleCloudsShaders.getError()).build();
+ saveAndPrintCrashReports(this.mc, this.initialInitializationResult);
+ return;
+ }
+
+ RendererInitializeResult compatError = SimpleCloudsCompatHelper.findCompatErrors();
+ if (compatError.getState() == RendererInitializeResult.State.ERROR)
+ {
+ LOGGER.error("Simple Clouds renderer could not initialize due to compat error(s): {}", compatError.getErrors().stream().map(e -> e.text().getString()).toList());
+ this.initialInitializationResult = compatError;
+ saveAndPrintCrashReports(this.mc, this.initialInitializationResult);
+ return;
+ }
+
+ StartupMessageManager.addModMessage("Initializing Simple Clouds renderer");
+
+ LOGGER.debug("OpenGL {}", openGlVersion);
+
+ Instant started = Instant.now();
+
+ LOGGER.debug("Beginning Simple Clouds renderer initialization");
+
+ this.failedToCopyDepthBuffer = false;
+
+ // --- Render Targets ---
+
+ boolean highPrecisionDepth = SimpleCloudsMod.dhLoaded();
+
+ RenderTarget main = SimpleCloudsCompatHelper.getMainRenderTarget();
+ if (main == null)
+ {
+ this.initialInitializationResult = RendererInitializeResult.builder().errorUnknown(new NullPointerException("Main framebuffer is null"), "Simple Clouds Renderer").build();
+ saveAndPrintCrashReports(this.mc, this.initialInitializationResult);
+ return;
+ }
+
+ if (this.cloudTarget != null)
+ this.cloudTarget.destroyBuffers();
+ this.cloudTarget = new CloudRenderTarget(main.width, main.height, Minecraft.ON_OSX, highPrecisionDepth);
+ this.cloudTarget.setClearColor(0.0F, 0.0F, 0.0F, 0.0F);
+
+ if (this.cloudTransparencyTarget != null)
+ this.cloudTransparencyTarget.destroyBuffers();
+ this.cloudTransparencyTarget = new WeightedBlendingTarget(main.width, main.height, Minecraft.ON_OSX, highPrecisionDepth);
+
+ this.stormFogResolutionDivisor = SimpleCloudsCompatHelper.getStormFogResolutionDivisor();
+ if (this.stormFogTarget != null)
+ this.stormFogTarget.destroyBuffers();
+ this.stormFogTarget = new TextureTarget(main.width / this.stormFogResolutionDivisor, main.height / this.stormFogResolutionDivisor, false, Minecraft.ON_OSX);
+ this.stormFogTarget.setClearColor(0.0F, 0.0F, 0.0F, 0.0F);
+ this.stormFogTarget.setFilterMode(GL11.GL_LINEAR);
+
+ if (this.blurTarget != null)
+ this.blurTarget.destroyBuffers();
+ this.blurTarget = new TextureTarget(main.width, main.height, false, Minecraft.ON_OSX);
+ this.blurTarget.setClearColor(0.0F, 0.0F, 0.0F, 0.0F);
+ this.blurTarget.setFilterMode(GL11.GL_LINEAR);
+
+ // --- Mesh Generator ---
+
+ this.setupMeshGenerator(); // Create/setup the generator
+ this.prepareMeshGenerator(0.0F); // Prepare it
+
+ RendererInitializeResult result = this.meshGenerator.init(manager); // Initialize
+ if (this.initialInitializationResult == null)
+ this.initialInitializationResult = result;
+
+ // --- Shadow Map ---
+
+ if (this.stormFogShadowMap != null)
+ {
+ this.stormFogShadowMap.close();
+ this.stormFogShadowMap = null;
+ }
+
+ this.shadowMap.ifPresent(buffer -> {
+ buffer.close();
+ });
+
+ int span = this.meshGenerator.getLodConfig().getEffectiveChunkSpan() * SimpleCloudsConstants.CHUNK_SIZE * SimpleCloudsConstants.CLOUD_SCALE;
+ this.stormFogShadowMap = new ShadowMapBuffer(span, span, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 0.0F, 10000.0F, true, false);
+
+ if (SimpleCloudsConfig.CLIENT.distantShadows.get() && SimpleCloudsMod.dhLoaded())
+ {
+ int distantShadowSpan = SimpleCloudsConfig.CLIENT.shadowDistance.get() * 2;
+ distantShadowSpan = Math.min(distantShadowSpan, span);
+ this.shadowMap = Optional.of(new ShadowMapBuffer(distantShadowSpan, distantShadowSpan, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 0.0F, 10000.0F, false, true));
+ }
+ else
+ {
+ this.shadowMap = Optional.empty();
+ }
+
+ // --- Post Processing Shaders ---
+
+ this.destroyPostChains();
+
+ if (this.lightningBoltPositions != null)
+ {
+ BindingManager.freeSSBO(this.lightningBoltPositions);
+ this.lightningBoltPositions = null;
+ }
+
+ this.lightningBoltPositions = BindingManager.createSSBO(GL15.GL_DYNAMIC_DRAW);
+ this.lightningBoltPositions.allocateBuffer(MAX_LIGHTNING_BOLTS * BYTES_PER_LIGHTNING_BOLT);
+
+ this.stormPostProcessing = this.createPostChain(manager, STORM_POST_PROCESSING_LOC, this.stormFogTarget, pass ->
+ {
+ EffectInstance effect = pass.getEffect();
+ effect.setSampler("ShadowMap", () -> this.stormFogShadowMap.getDepthTexId());
+ effect.setSampler("ShadowMapColor", () -> this.stormFogShadowMap.getColorTexId());
+ effect.setSampler("DepthSampler", () -> this.cloudTarget.getDepthTextureId());
+ this.lightningBoltPositions.optionalBindToProgram("LightningBolts", effect.getId());
+ });
+
+ this.blurPostProcessing = this.createPostChain(manager, BLUR_POST_PROCESSING_LOC, this.blurTarget);
+ this.blurPostProcessing.getTempTarget("swap").setFilterMode(GL11.GL_LINEAR);
+
+ this.screenSpaceWorldFog = this.createPostChain(manager, SCREEN_SPACE_WORLD_FOG_LOC, main, pass ->
+ {
+ EffectInstance effect = pass.getEffect();
+ effect.setSampler("StormFogSampler", () -> this.blurTarget.getColorTextureId());
+ effect.setSampler("CloudDepthSampler", () -> this.cloudTarget.getDepthTextureId());
+ });
+
+ this.finalComposite = this.createPostChain(manager, this.settings.useTransparency() ? FINAL_COMPOSITE_LOC : FINAL_COMPOSITE_NO_TRANSPARENCY_LOC, main, pass ->
+ {
+ EffectInstance effect = pass.getEffect();
+ if (this.settings.useTransparency())
+ {
+ effect.setSampler("AccumTexture", () -> this.cloudTransparencyTarget.getColorTextureId());
+ effect.setSampler("RevealageTexture", () -> this.cloudTransparencyTarget.getRevealageTextureId());
+ }
+ effect.setSampler("CloudsTexture", () -> this.cloudTarget.getColorTextureId());
+ });
+
+ if (this.shadowMap.isPresent())
+ {
+ ShadowMapBuffer map = this.shadowMap.get();
+ this.cloudShadows = this.createPostChain(manager, CLOUD_SHADOWS_LOC, main, pass ->
+ {
+ EffectInstance effect = pass.getEffect();
+ effect.setSampler("ShadowMap", () -> this.shadowMap.get().getDepthTexId());
+ effect.safeGetUniform("ShadowSpan").set((float)Math.min(map.getViewWidth(), map.getViewHeight()));
+ });
+ }
+
+ this.atmoshpericClouds.init(manager);
+
+ // --- Final debug ---
+
+ long duration = Duration.between(started, Instant.now()).toMillis();
+ LOGGER.info("Finished initialization, took {} ms", duration);
+
+ LOGGER.debug("Total LODs: {}", this.meshGenerator.getLodConfig().getLods().length + 1);
+ LOGGER.debug("Highest detail (primary) chunk span: {}", this.meshGenerator.getLodConfig().getPrimaryChunkSpan());
+ LOGGER.debug("Effective chunk span with LODs (total viewable area): {}", this.meshGenerator.getLodConfig().getEffectiveChunkSpan());
+ LOGGER.debug("Total span in blocks: {}", this.meshGenerator.getLodConfig().getEffectiveChunkSpan() * SimpleCloudsConstants.CHUNK_SIZE * SimpleCloudsConstants.CLOUD_SCALE);
+
+ //Print crash reports if needed
+ saveAndPrintCrashReports(this.mc, result);
+ }
+
+ private static void saveAndPrintCrashReports(Minecraft mc, RendererInitializeResult result)
+ {
+ switch (result.getState())
+ {
+ case ERROR:
+ {
+ List reports = result.createCrashReports();
+ LOGGER.error("---------CRASH REPORT BEGIN---------");
+ for (CrashReport report : reports)
+ {
+ mc.fillReport(report);
+ LOGGER.error("{}", report.getFriendlyReport());
+ }
+ LOGGER.error("---------CRASH REPORT END---------");
+ result.saveCrashReports(mc.gameDirectory);
+ break;
+ }
+ default:
+ }
+ }
+
+ private void setupMeshGenerator()
+ {
+ if (this.settings.checkAndOrBeginInitialization(this.meshGenerator))
+ {
+ if (this.meshGenerator != null)
+ {
+ this.meshGenerator.close(); //Close the current generator
+ this.meshGenerator = null;
+ }
+
+ CloudMode mode = this.settings.getCurrentCloudMode();
+ boolean isAmbientMode = mode == CloudMode.AMBIENT;
+ boolean useMultiRegion = isAmbientMode || mode == CloudMode.DEFAULT;
+ boolean shadedClouds = this.settings.shadedClouds();
+ boolean useFixedMeshDataSectionSize = this.settings.useFixedMeshDataSectionSize();
+ boolean useTransparency = this.settings.useTransparency();
+ LevelOfDetailConfig lod = this.settings.getCurrentLod().getConfig();
+
+ var builder = CloudMeshGenerator.builder()
+ .fadeNearOrigin(isAmbientMode)
+ .shadedClouds(shadedClouds)
+ .fixedMeshDataSectionSize(useFixedMeshDataSectionSize)
+ .meshGenInterval(SimpleCloudsRenderer::calculateMeshGenInterval)
+ .lodConfig(lod)
+ .useTransparency(useTransparency);
+
+ if (useMultiRegion) //Use the multi-region generator for DEFAULT or AMBIENT cloud mode
+ {
+ if (isAmbientMode)
+ {
+ builder.fadeStart(SimpleCloudsConstants.AMBIENT_MODE_FADE_START)
+ .fadeEnd(SimpleCloudsConstants.AMBIENT_MODE_FADE_END);
+ }
+ this.meshGenerator = builder.createMultiRegion();
+ }
+ else if (mode == CloudMode.SINGLE)
+ {
+ float fadeStart = (float)SimpleCloudsConfig.CLIENT.singleModeFadeStartPercentage.get() / 100.0F;
+ float fadeEnd = (float)SimpleCloudsConfig.CLIENT.singleModeFadeEndPercentage.get() / 100.0F;
+ this.meshGenerator = builder.fadeStart(fadeStart).fadeEnd(fadeEnd).createSingleRegion(SimpleCloudsConstants.EMPTY);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Not sure how to handle cloud mode " + mode);
+ }
+ }
+
+ if (this.meshGenerator instanceof MultiRegionCloudMeshGenerator multiRegionGenerator)
+ {
+ multiRegionGenerator.setCloudGetter(this.cloudManager != null ? this.cloudManager : CloudGetter.EMPTY);
+ }
+ else if (this.meshGenerator instanceof SingleRegionCloudMeshGenerator singleRegionGenerator)
+ {
+ //Find the desired single mode cloud type, either from the client-side only context or
+ //from the synced cloud types from the server
+ CloudType type = this.settings.getSingleModeCloudType();
+ if (!ClientCloudManager.isAvailableServerSide() && !ClientSideCloudTypeManager.isValidClientSideSingleModeCloudType(type))
+ type = SimpleCloudsConstants.EMPTY;
+ if (type == null)
+ type = SimpleCloudsConstants.EMPTY;
+ singleRegionGenerator.setCloudType(type);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Not sure how to handle generator: " + this.meshGenerator);
+ }
+ }
+
+ private void destroyPostChains()
+ {
+ this.postChains.forEach(PostChain::close);
+ this.postChains.clear();
+ }
+
+ private @Nullable PostChain createPostChain(ResourceManager manager, ResourceLocation loc, RenderTarget target)
+ {
+ return this.createPostChain(manager, loc, target, effect -> {});
+ }
+
+ private @Nullable PostChain createPostChain(ResourceManager manager, ResourceLocation loc, RenderTarget target, Consumer passConsumer)
+ {
+ try
+ {
+ PostChain chain = new PostChain(this.mc.getTextureManager(), manager, target, loc);
+ chain.resize(target.width, target.height);
+ for (PostPass pass : ((MixinPostChain)chain).simpleclouds$getPostPasses())
+ passConsumer.accept(pass);
+ this.postChains.add(chain);
+ return chain;
+ }
+ catch (JsonSyntaxException e)
+ {
+ LOGGER.warn("Failed to parse post shader: {}", loc, e);
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("Failed to load post shader: {}", loc, e);
+ }
+
+ return null;
+ }
+
+ public void onMainWindowResize(int width, int height)
+ {
+ this.atmoshpericClouds.onResize(width, height);
+
+ RenderTarget main = SimpleCloudsCompatHelper.getMainRenderTarget();
+ if (main == null)
+ return;
+
+ width = main.width;
+ height = main.height;
+
+ if (this.cloudTarget != null)
+ this.cloudTarget.resize(width, height, Minecraft.ON_OSX);
+
+ if (this.cloudTransparencyTarget != null)
+ this.cloudTransparencyTarget.resize(width, height, Minecraft.ON_OSX);
+
+ this.stormFogResolutionDivisor = SimpleCloudsCompatHelper.getStormFogResolutionDivisor();
+
+ if (this.stormFogTarget != null)
+ {
+ this.stormFogTarget.resize(width / this.stormFogResolutionDivisor, height / this.stormFogResolutionDivisor, Minecraft.ON_OSX);
+ this.stormFogTarget.setFilterMode(GL11.GL_LINEAR);
+ }
+
+ if (this.blurTarget != null)
+ {
+ this.blurTarget.resize(width, height, Minecraft.ON_OSX);
+ this.blurTarget.setFilterMode(GL11.GL_LINEAR);
+ }
+
+ for (PostChain chain : this.postChains)
+ {
+ RenderTarget chainTarget = ((MixinPostChain)chain).simpleclouds$getScreenTarget();
+ chain.resize(chainTarget.width, chainTarget.height);
+ }
+
+ if (this.blurPostProcessing != null)
+ this.blurPostProcessing.getTempTarget("swap").setFilterMode(GL11.GL_LINEAR);
+ }
+
+ public void shutdown()
+ {
+ if (this.cloudTarget != null)
+ this.cloudTarget.destroyBuffers();
+ if (this.cloudTransparencyTarget != null)
+ this.cloudTransparencyTarget.destroyBuffers();
+ if (this.stormFogTarget != null)
+ this.stormFogTarget.destroyBuffers();;
+ if (this.blurTarget != null)
+ this.blurTarget.destroyBuffers();
+
+ this.cloudTarget = null;
+ this.cloudTransparencyTarget = null;
+ this.stormFogTarget = null;
+ this.blurTarget = null;
+
+ this.destroyPostChains();
+
+ if (this.meshGenerator != null)
+ this.meshGenerator.close();
+
+ if (this.stormFogShadowMap != null)
+ {
+ this.stormFogShadowMap.close();
+ this.stormFogShadowMap = null;
+ }
+
+ if (this.shadowMap.isPresent())
+ {
+ this.shadowMap.get().close();
+ this.shadowMap = Optional.empty();
+ }
+
+ if (this.lightningBoltPositions != null)
+ {
+ BindingManager.freeSSBO(this.lightningBoltPositions);
+ this.lightningBoltPositions = null;
+ }
+
+ this.atmoshpericClouds.close();
+ }
+
+ public void baseTick()
+ {
+ if (this.needsReload)
+ {
+ this.onResourceManagerReload(this.mc.getResourceManager());
+ this.needsReload = false;
+ }
+ }
+
+ public void tick()
+ {
+ this.worldEffectsManager.tick();
+
+ if (this.cloudManager != null)
+ this.atmoshpericClouds.setWindDirection(this.cloudManager.calculateWindDirection());
+ this.atmoshpericClouds.tick();
+
+ if (this.meshGenerator != null)
+ this.meshGenerator.worldTick();
+ }
+
+ public static void renderCloudsOpaque(CloudMeshGenerator generator, PoseStack stack, Matrix4f projMat, float fogStart, float fogEnd, float partialTick, float r, float g, float b, @Nullable Frustum frustum)
+ {
+ renderCloudsOpaque(generator, stack, projMat, fogStart, fogEnd, partialTick, r, g, b, frustum, true);
+ }
+
+ public static void renderCloudsOpaque(CloudMeshGenerator generator, PoseStack stack, Matrix4f projMat, float fogStart, float fogEnd, float partialTick, float r, float g, float b, @Nullable Frustum frustum, boolean ditherFade)
+ {
+ RenderSystem.assertOnRenderThread();
+
+ BufferUploader.reset();
+
+ if (!generator.canRender())
+ return;
+
+ RenderSystem.disableBlend();
+ RenderSystem.enableDepthTest();
+ RenderSystem.disableCull();
+
+ SingleSSBOShaderInstance shader = SimpleCloudsShaders.getCloudsShader();
+ RenderSystem.setShader(() -> shader);
+
+ TextureManager manager = Minecraft.getInstance().getTextureManager();
+ AbstractTexture ditherTexture = manager.getTexture(DITHER_TEXTURE);
+ shader.setSampler("BayerMatrixSampler", ditherTexture);
+ shader.safeGetUniform("DitherScale").set(DITHER_SCALE);
+
+ SimpleCloudsRenderer.prepareShader(shader, stack.last().pose(), projMat, fogStart, fogEnd);
+ shader.apply();
+
+ generator.forRenderableMeshChunks(frustum, MeshChunk::getOpaqueBuffers, (chunk, opaqueBuffers) ->
+ {
+ if (ditherFade)
+ {
+ RenderSystem.setShaderColor(r, g, b, chunk.getAlpha(partialTick));
+ shader.COLOR_MODULATOR.set(RenderSystem.getShaderColor());
+ shader.COLOR_MODULATOR.upload();
+ }
+ GL30.glBindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, shader.getShaderStorageBinding(), opaqueBuffers.getBufferId());
+ generator.getSideMesh().drawInstanced(opaqueBuffers.getElementCount());
+ }, ditherFade);
+ GL30.glBindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, shader.getShaderStorageBinding(), 0);
+
+ shader.clear();
+
+ GL30.glBindVertexArray(0);
+
+ RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
+ RenderSystem.enableCull();
+ }
+
+ public static void renderCloudsTransparency(CloudMeshGenerator generator, PoseStack stack, Matrix4f projMat, float fogStart, float fogEnd, float partialTick, float r, float g, float b, @Nullable Frustum frustum)
+ {
+ renderCloudsTransparency(generator, stack, projMat, fogStart, fogEnd, partialTick, r, g, b, frustum, true);
+ }
+
+ public static void renderCloudsTransparency(CloudMeshGenerator generator, PoseStack stack, Matrix4f projMat, float fogStart, float fogEnd, float partialTick, float r, float g, float b, @Nullable Frustum frustum, boolean ditherFade)
+ {
+ RenderSystem.assertOnRenderThread();
+
+ BufferUploader.reset();
+
+ if (!generator.canRender() || !generator.transparencyEnabled())
+ return;
+
+ RenderSystem.enableDepthTest();
+ RenderSystem.depthMask(false);
+
+ SingleSSBOShaderInstance shader = SimpleCloudsShaders.getCloudsTransparencyShader();
+ RenderSystem.setShader(() -> shader);
+
+ TextureManager manager = Minecraft.getInstance().getTextureManager();
+ AbstractTexture ditherTexture = manager.getTexture(DITHER_TEXTURE);
+ shader.setSampler("BayerMatrixSampler", ditherTexture);
+ shader.safeGetUniform("DitherScale").set(DITHER_SCALE);
+
+ SimpleCloudsRenderer.prepareShader(shader, stack.last().pose(), projMat, fogStart, fogEnd);
+
+ shader.apply();
+
+ GL30.glEnablei(GL11.GL_BLEND, 0);
+ GL30.glEnablei(GL11.GL_BLEND, 1);
+ GL40.glBlendEquationi(0, GL14.GL_FUNC_ADD);
+ GL40.glBlendEquationi(1, GL14.GL_FUNC_ADD);
+ GL40.glBlendFunci(0, GL11.GL_ONE, GL11.GL_ONE);
+ GL40.glBlendFunci(1, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_COLOR);
+
+ generator.forRenderableMeshChunks(frustum, c -> c.getTransparentBuffers().get(), (chunk, transparentBuffers) ->
+ {
+ if (ditherFade)
+ {
+ RenderSystem.setShaderColor(r, g, b, chunk.getAlpha(partialTick));
+ shader.COLOR_MODULATOR.set(RenderSystem.getShaderColor());
+ shader.COLOR_MODULATOR.upload();
+ }
+
+ GL30.glBindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, shader.getShaderStorageBinding(), transparentBuffers.getBufferId());
+ generator.getCubeMesh().drawInstanced(transparentBuffers.getElementCount());
+ }, ditherFade);
+ GL30.glBindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, shader.getShaderStorageBinding(), 0);
+
+ shader.clear();
+
+ GL30.glDisablei(GL11.GL_BLEND, 0);
+ GL30.glDisablei(GL11.GL_BLEND, 1);
+ GL40.glBlendFuncSeparatei(0, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
+ GL40.glBlendFuncSeparatei(1, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
+
+ GL30.glBindVertexArray(0);
+
+ RenderSystem.depthMask(true);
+
+ RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
+ }
+
+ private PoseStack createShadowMapStack(ShadowMapBuffer shadowMap, double camX, double camY, double camZ, Consumer transformApplier)
+ {
+ PoseStack stack = new PoseStack();
+ stack.setIdentity();
+ double depthCenter = ((double)shadowMap.getNear() + (double)shadowMap.getFar()) * -0.5D;
+ stack.translate((double)shadowMap.getViewWidth() / 2.0D, (double)shadowMap.getViewHeight() / 2.0D, depthCenter);
+ transformApplier.accept(stack);
+ float chunkSizeUpscaled = (float)SimpleCloudsConstants.CHUNK_SIZE * (float)SimpleCloudsConstants.CLOUD_SCALE;
+ float camOffsetX = ((float)Mth.floor(camX / chunkSizeUpscaled) * chunkSizeUpscaled);
+ float camOffsetZ = ((float)Mth.floor(camZ / chunkSizeUpscaled) * chunkSizeUpscaled);
+ stack.translate(-camOffsetX, -(double)this.cloudManager.getCloudHeight(), -camOffsetZ);
+ return stack;
+ }
+
+ private void renderShadowMap(ShadowMapBuffer shadowMap, PoseStack stack, SingleSSBOShaderInstance shader, @Nullable Frustum frustum)
+ {
+ RenderSystem.assertOnRenderThread();
+
+ stack.pushPose();
+ this.translateClouds(stack, 0.0D, 0.0D, 0.0D);
+
+ RenderSystem.setShader(() -> shader);
+ prepareShader(shader, stack.last().pose(), shadowMap.getProjMatrix(), this.fogStart, this.fogEnd);
+ shader.apply();
+
+ shadowMap.bind();
+ shadowMap.clear(Minecraft.ON_OSX);
+
+ this.meshGenerator.forRenderableMeshChunks(frustum, MeshChunk::getOpaqueBuffers, (chunk, opaqueBuffers) ->
+ {
+ GL30.glBindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, shader.getShaderStorageBinding(), opaqueBuffers.getBufferId());
+ this.meshGenerator.getSideMesh().drawInstanced(opaqueBuffers.getElementCount());
+ });
+ GL30.glBindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, shader.getShaderStorageBinding(), 0);
+ GL30.glBindVertexArray(0);
+
+ shadowMap.unbind();
+
+ shader.clear();
+
+ stack.popPose();
+ }
+
+ private float determineShadowMapAngle(float partialTick)
+ {
+ float timeOfDay = this.mc.level.getTimeOfDay(partialTick);
+ return 45.0F * Mth.sin(2.0F * (float)Math.PI * timeOfDay);
+ }
+
+ private void renderShadowMaps(double camX, double camY, double camZ, float partialTick)
+ {
+ RenderSystem.assertOnRenderThread();
+
+ BufferUploader.reset();
+
+ RenderSystem.disableBlend();
+ RenderSystem.enableDepthTest();
+ RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
+ RenderSystem.disableCull();
+
+ this.stormFogShadowMapStack = this.createShadowMapStack(this.stormFogShadowMap, camX, camY, camZ, s ->
+ {
+ Vector2f direction = this.cloudManager.calculateWindDirection();
+ float yaw = (float)Mth.atan2((double)direction.x, (double)direction.y);
+ s.mulPose(Axis.XP.rotationDegrees(SimpleCloudsConfig.CLIENT.stormFogAngle.get().floatValue()));
+ s.mulPose(Axis.YP.rotation(yaw));
+ });
+ this.renderShadowMap(this.stormFogShadowMap, this.stormFogShadowMapStack, SimpleCloudsShaders.getStormFogShadowMapShader(), this.cullFrustum);
+
+ this.shadowMapStack = this.shadowMap.map(buffer ->
+ {
+ PoseStack stack = this.createShadowMapStack(buffer, camX, camY, camZ, s -> {
+ s.mulPose(Axis.XP.rotationDegrees(90.0F));
+ s.mulPose(Axis.ZN.rotationDegrees(this.determineShadowMapAngle(partialTick)));
+ });
+ this.renderShadowMap(buffer, stack, SimpleCloudsShaders.getCloudsShadowMapShader(), null);
+ return stack;
+ }).orElse(null);
+
+ RenderSystem.enableCull();
+
+ this.mc.getMainRenderTarget().bindWrite(true);
+ }
+
+ public static void renderCloudsDebug(CloudMeshGenerator generator, PoseStack stack, Matrix4f projMat, float partialTick, float fogStart, float fogEnd, @Nullable Frustum frustum, boolean chunkBoundaries, boolean noiseBoundaries)
+ {
+ RenderSystem.assertOnRenderThread();
+
+ if (!generator.canRender())
+ return;
+
+ BufferUploader.reset();
+
+ RenderSystem.disableBlend();
+ RenderSystem.enableDepthTest();
+ RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
+ RenderSystem.disableCull();
+
+ Tesselator tesselator = Tesselator.getInstance();
+ BufferBuilder builder = tesselator.getBuilder();
+ builder.begin(VertexFormat.Mode.LINES, DefaultVertexFormat.POSITION_COLOR_NORMAL);
+
+ generator.forRenderableMeshChunks(frustum, MeshChunk::getOpaqueBuffers, (chunk, bufferSet) ->
+ {
+ PreparedChunk preparedChunk = chunk.getChunkInfo();
+ if (chunkBoundaries)
+ {
+ int color = Color.HSBtoRGB((float)preparedChunk.lodLevel() / ((float)generator.getLodConfig().getLods().length + 1), 1.0F, 1.0F);
+ float r = (float)FastColor.ARGB32.red(color) / 255.0F;
+ float g = (float)FastColor.ARGB32.green(color) / 255.0F;
+ float b = (float)FastColor.ARGB32.blue(color) / 255.0F;
+ LevelRenderer.renderLineBox(builder, chunk.getBoundsMinX() + 1.0F, chunk.getBoundsMinY() + 1.0F, chunk.getBoundsMinZ() + 1.0F, chunk.getBoundsMaxX() - 1.0F, chunk.getBoundsMaxY() - 1.0F, chunk.getBoundsMaxZ() - 1.0F, r, g, b, 1.0F);
+ }
+ if (noiseBoundaries)
+ LevelRenderer.renderLineBox(builder, chunk.getBoundsMinX() + 1.0F, chunk.getMinHeight() + 1.0F, chunk.getBoundsMinZ() + 1.0F, chunk.getBoundsMaxX() - 1.0F, chunk.getMaxHeight() - 1.0F, chunk.getBoundsMaxZ() - 1.0F, 1.0F, 1.0F, 0.0F, 1.0F);
+ });
+
+ RenderSystem.setShader(GameRenderer::getRendertypeLinesShader);
+ ShaderInstance shader = RenderSystem.getShader();
+ SimpleCloudsRenderer.prepareShader(shader, stack.last().pose(), projMat, fogStart, fogEnd);
+ shader.LINE_WIDTH.set(2.5F);
+ shader.FOG_START.set(Float.MAX_VALUE);
+ shader.apply();
+ BufferUploader.draw(builder.end());
+ shader.clear();
+
+ RenderSystem.enableCull();
+
+ RenderSystem.defaultBlendFunc();
+ RenderSystem.enableBlend();
+
+ builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
+
+ generator.forRenderableMeshChunks(frustum, MeshChunk::getOpaqueBuffers, (chunk, bufferSet) ->
+ {
+ PreparedChunk preparedChunk = chunk.getChunkInfo();
+ if (chunkBoundaries)
+ {
+ int color = Color.HSBtoRGB((float)preparedChunk.lodLevel() / ((float)generator.getLodConfig().getLods().length + 1), 1.0F, 1.0F);
+ float r = (float)FastColor.ARGB32.red(color) / 255.0F;
+ float g = (float)FastColor.ARGB32.green(color) / 255.0F;
+ float b = (float)FastColor.ARGB32.blue(color) / 255.0F;
+ renderChunkBox(builder, chunk.getBoundsMinX() + 1.0F, chunk.getBoundsMinY() + 1.0F, chunk.getBoundsMinZ() + 1.0F, chunk.getBoundsMaxX() - 1.0F, chunk.getBoundsMaxY() - 1.0F, chunk.getBoundsMaxZ() - 1.0F, r, g, b, 0.4F);
+ }
+ if (noiseBoundaries)
+ renderChunkBox(builder, chunk.getBoundsMinX() + 1.0F, chunk.getMinHeight() + 1.0F, chunk.getBoundsMinZ() + 1.0F, chunk.getBoundsMaxX() - 1.0F, chunk.getMaxHeight() - 1.0F, chunk.getBoundsMaxZ() - 1.0F, 1.0F, 1.0F, 0.0F, 0.4F);
+ });
+
+ RenderSystem.setShader(GameRenderer::getPositionColorShader);
+ shader = RenderSystem.getShader();
+ SimpleCloudsRenderer.prepareShader(shader, stack.last().pose(), projMat, fogStart, fogEnd);
+ shader.apply();
+ BufferUploader.draw(builder.end());
+ shader.clear();
+
+ RenderSystem.disableBlend();
+ }
+
+ public float[] getCloudColor(float partialTick)
+ {
+ Vec3 cloudCol = this.mc.level.getCloudColor(partialTick);
+ float factor = this.worldEffectsManager.getDarkenFactor(partialTick, 0.8F);
+ float skyFlashFactor = Math.max(0.0F, ((float)this.mc.level.getSkyFlashTime() - partialTick) * SimpleCloudsConstants.LIGHTNING_FLASH_STRENGTH);
+ factor += skyFlashFactor;
+ float r = Mth.clamp((float)cloudCol.x * factor, 0.0F, 1.0F);
+ float g = Mth.clamp((float)cloudCol.y * factor, 0.0F, 1.0F);
+ float b = Mth.clamp((float)cloudCol.z * factor, 0.0F, 1.0F);
+ return new float[] { r, g, b };
+ }
+
+ public void translateClouds(PoseStack stack, double camX, double camY, double camZ)
+ {
+ stack.translate(-camX, -camY + (double)this.cloudManager.getCloudHeight(), -camZ);
+ stack.scale((float)SimpleCloudsConstants.CLOUD_SCALE, (float)SimpleCloudsConstants.CLOUD_SCALE, (float)SimpleCloudsConstants.CLOUD_SCALE);
+ }
+
+ public void renderWeather(LightTexture texture, float partialTick, double camX, double camY, double camZ)
+ {
+ if (SimpleCloudsCompatHelper.renderCustomRain())
+ this.worldEffectsManager.renderRain(texture, partialTick, camX, camY, camZ);
+ if (!SimpleCloudsMod.dhLoaded())
+ this.worldEffectsManager.renderLightning(partialTick, camX, camY, camZ);
+ }
+
+ public void renderBeforeLevel(PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ)
+ {
+ if (!SimpleCloudsCompatHelper.renderThisPass())
+ return;
+
+ CloudsRenderPipeline pipeline = CompatHelper.areShadersRunning() ? CloudsRenderPipeline.SHADER_SUPPORT : CloudsRenderPipeline.DEFAULT;
+ DetermineCloudRenderPipelineEvent pipelineEvent = new DetermineCloudRenderPipelineEvent(pipeline);
+ MinecraftForge.EVENT_BUS.post(pipelineEvent);
+ this.renderPipelineThisPass = pipeline;
+ if (pipelineEvent.getOverridenPipeline() != null)
+ this.renderPipelineThisPass = pipelineEvent.getOverridenPipeline();
+
+ float factor = this.worldEffectsManager.getDarkenFactor(partialTick);
+ float renderDistance = (float)this.meshGenerator.getCloudAreaMaxRadius() * (float)SimpleCloudsConstants.CLOUD_SCALE * factor;
+ if (renderDistance < 2867.0F)
+ renderDistance = 2867.0F;
+ ModifyCloudRenderDistanceEvent renderDistEvent = new ModifyCloudRenderDistanceEvent(renderDistance);
+ MinecraftForge.EVENT_BUS.post(renderDistEvent);
+ renderDistance = renderDistEvent.getRenderDistance();
+ this.fogStart = renderDistance / 4.0F;
+ this.fogEnd = renderDistance;
+
+ Entity cameraEntity = this.mc.gameRenderer.getMainCamera().getEntity();
+ if (cameraEntity instanceof LivingEntity living)
+ {
+ var map = living.getActiveEffectsMap();
+ if (map.containsKey(MobEffects.BLINDNESS))
+ {
+ MobEffectInstance instance = map.get(MobEffects.BLINDNESS);
+ float effectFactor = instance.isInfiniteDuration() ? 5.0F : Mth.lerp(Math.min(1.0F, (float)instance.getDuration() / 20.0F), renderDistance, 5.0F);
+ this.fogStart = 0.0F;
+ this.fogEnd = effectFactor * 0.8F;
+ }
+ else if (map.containsKey(MobEffects.DARKNESS))
+ {
+ MobEffectInstance instance = map.get(MobEffects.DARKNESS);
+ if (instance.getFactorData().isPresent())
+ {
+ float f = Mth.lerp(instance.getFactorData().get().getFactor(living, partialTick), renderDistance, 15.0F);
+ this.fogStart = 0.0F;
+ this.fogEnd = f;
+ }
+ }
+ }
+
+ this.meshGenerator.setCullDistance(this.fogEnd / (float)SimpleCloudsConstants.CLOUD_SCALE);
+
+ this.mc.getProfiler().push("simple_clouds_prepare");
+
+ this.cullFrustum = new Frustum(stack.last().pose(), projMat);
+ float scale = (float)SimpleCloudsConstants.CLOUD_SCALE;
+ double originX = camX / scale;
+ double originY = (camY - (double)this.cloudManager.getCloudHeight()) / scale;
+ double originZ = camZ / scale;
+ this.cullFrustum.prepare(originX, originY, originZ);
+
+ ProfilerFiller p = this.mc.getProfiler();
+
+ if (SimpleCloudsConfig.CLIENT.generateMesh.get() && SimpleCloudsCompatHelper.isPrimaryPass())
+ {
+ p.push("mesh_generation");
+ this.prepareMeshGenerator(partialTick);
+ this.meshGenerator.genTick(originX, originY, originZ, SimpleCloudsConfig.CLIENT.frustumCulling.get() ? this.cullFrustum : null, partialTick);
+ p.pop();
+ }
+
+ if (SimpleCloudsConfig.CLIENT.renderClouds.get() && SimpleCloudsCompatHelper.isPrimaryPass())
+ {
+ p.push("shadow_map");
+ this.renderShadowMaps(camX, camY, camZ, partialTick);
+ this.getRenderPipeline().prepare(this.mc, this, stack, projMat, partialTick, camX, camY, camZ, this.cullFrustum);
+ p.pop();
+ }
+
+ this.mc.getProfiler().pop();
+ }
+
+ public void renderAfterSky(PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ)
+ {
+ if (!SimpleCloudsCompatHelper.renderThisPass())
+ return;
+
+ this.mc.getProfiler().push("simple_clouds_after_sky");
+ this.getRenderPipeline().afterSky(this.mc, this, stack, projMat, partialTick, camX, camY, camZ, this.cullFrustum);
+ this.mc.getProfiler().pop();
+ }
+
+ public void renderBeforeWeather(PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ)
+ {
+ if (!SimpleCloudsCompatHelper.renderThisPass())
+ return;
+
+ this.mc.getProfiler().push("simple_clouds_before_weather");
+ this.getRenderPipeline().beforeWeather(this.mc, this, stack, projMat, partialTick, camX, camY, camZ, this.cullFrustum);
+ this.mc.getProfiler().pop();
+ }
+
+ public void renderAfterLevel(PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ)
+ {
+ if (!SimpleCloudsCompatHelper.renderThisPass())
+ return;
+
+ this.mc.getProfiler().push("simple_clouds");
+ this.getRenderPipeline().afterLevel(this.mc, this, stack, projMat, partialTick, camX, camY, camZ, this.cullFrustum);
+ this.mc.getProfiler().pop();
+
+ this.mc.getProfiler().push("world_effects");
+ this.worldEffectsManager.renderPost(stack, partialTick, camX, camY, camZ, (float)SimpleCloudsConstants.CLOUD_SCALE);
+ this.mc.getProfiler().pop();
+ }
+
+ public void doBlurPostProcessing(float partialTick)
+ {
+ if (this.blurPostProcessing != null)
+ {
+ RenderSystem.disableDepthTest();
+ RenderSystem.resetTextureMatrix();
+ RenderSystem.disableBlend();
+ RenderSystem.depthMask(false);
+ this.blurPostProcessing.process(partialTick);
+ RenderSystem.depthMask(true);
+ }
+ }
+
+ public void doScreenSpaceWorldFog(PoseStack stack, Matrix4f projMat, float partialTick)
+ {
+ if (this.screenSpaceWorldFog != null)
+ {
+ RenderSystem.disableBlend();
+ RenderSystem.disableDepthTest();
+ RenderSystem.resetTextureMatrix();
+ RenderSystem.depthMask(false);
+
+ Matrix4f invertedProjMat = new Matrix4f(projMat).invert();
+ Matrix4f invertedModelViewMat = new Matrix4f(stack.last().pose()).invert();
+ for (PostPass pass : ((MixinPostChain)this.screenSpaceWorldFog).simpleclouds$getPostPasses())
+ {
+ EffectInstance effect = pass.getEffect();
+ effect.safeGetUniform("InverseWorldProjMat").set(invertedProjMat);
+ effect.safeGetUniform("InverseModelViewMat").set(invertedModelViewMat);
+ effect.safeGetUniform("FogStart").set(RenderSystem.getShaderFogStart());
+ effect.safeGetUniform("FogEnd").set(RenderSystem.getShaderFogEnd());
+ float[] fogCol = RenderSystem.getShaderFogColor();
+ effect.safeGetUniform("FogColor").set(fogCol[0], fogCol[1], fogCol[2]);
+ effect.safeGetUniform("FogShape").set(RenderSystem.getShaderFogShape().getIndex());
+ }
+
+ this.screenSpaceWorldFog.process(partialTick);
+
+ RenderSystem.depthMask(true);
+ }
+ }
+
+ public void doFinalCompositePass(PoseStack stack, float partialTick, Matrix4f projMat)
+ {
+ if (this.finalComposite != null)
+ {
+ RenderSystem.disableDepthTest();
+ RenderSystem.resetTextureMatrix();
+ RenderSystem.depthMask(false);
+
+ this.finalComposite.process(partialTick);
+
+ RenderSystem.depthMask(true);
+ }
+ }
+
+ public void doStormPostProcessing(PoseStack stack, float partialTick, Matrix4f projMat, double camX, double camY, double camZ, float r, float g, float b)
+ {
+ if (this.stormPostProcessing == null || this.stormFogShadowMapStack == null || this.stormFogShadowMapStack == null)
+ return;
+
+ RenderSystem.disableBlend();
+ RenderSystem.disableDepthTest();
+ RenderSystem.resetTextureMatrix();
+ RenderSystem.depthMask(false);
+
+ this.stormFogTarget.clear(Minecraft.ON_OSX);
+ this.stormFogTarget.bindWrite(true);
+
+ MutableInt size = new MutableInt();
+ boolean flag = SimpleCloudsConfig.CLIENT.stormFogLightningFlashes.get();
+ if (flag)
+ {
+ List lightningBolts = this.worldEffectsManager.getLightningBolts();
+ size.setValue(Math.min(lightningBolts.size(), MAX_LIGHTNING_BOLTS));
+ if (size.getValue() > 0)
+ {
+ this.lightningBoltPositions.writeData(buffer ->
+ {
+ for (int i = 0; i < size.getValue(); i++)
+ {
+ LightningBolt bolt = lightningBolts.get(i);
+ Vector3f pos = bolt.getPosition();
+ buffer.putFloat(pos.x);
+ buffer.putFloat(pos.y);
+ buffer.putFloat(pos.z);
+ buffer.putFloat(bolt.getFade(partialTick));
+ }
+ buffer.rewind();
+ }, size.getValue() * BYTES_PER_LIGHTNING_BOLT, false);
+ }
+ }
+
+ Matrix4f invertedProjMat = new Matrix4f(projMat).invert();
+ Matrix4f invertedModelViewMat = new Matrix4f(stack.last().pose()).invert();
+ for (PostPass pass : ((MixinPostChain)this.stormPostProcessing).simpleclouds$getPostPasses())
+ {
+ EffectInstance effect = pass.getEffect();
+ effect.safeGetUniform("InverseWorldProjMat").set(invertedProjMat);
+ effect.safeGetUniform("InverseModelViewMat").set(invertedModelViewMat);
+ effect.safeGetUniform("ShadowProjMat").set(this.stormFogShadowMap.getProjMatrix());
+ effect.safeGetUniform("ShadowModelViewMat").set(this.stormFogShadowMapStack.last().pose());
+ effect.safeGetUniform("CameraPos").set((float)camX, (float)camY, (float)camZ);
+ effect.safeGetUniform("FogStart").set(this.fogEnd / 2.0F);
+ effect.safeGetUniform("FogEnd").set(this.fogEnd);
+ effect.safeGetUniform("ColorModulator").set(r, g, b, 1.0F);
+ float factor = this.worldEffectsManager.getDarkenFactor(partialTick);
+ effect.safeGetUniform("CutoffDistance").set(1000.0F * factor);
+ effect.safeGetUniform("TotalLightningBolts").set(size.getValue());
+ }
+
+ this.stormPostProcessing.process(partialTick);
+
+ RenderSystem.depthMask(true);
+ }
+
+ public void doCloudShadowProcessing(PoseStack stack, float partialTick, Matrix4f projMat, double camX, double camY, double camZ, int depthBufferId)
+ {
+ if (this.cloudShadows == null || this.shadowMap.isEmpty() || this.shadowMapStack == null)
+ return;
+
+ RenderSystem.disableBlend();
+ RenderSystem.disableDepthTest();
+ RenderSystem.resetTextureMatrix();
+ RenderSystem.depthMask(false);
+
+ Matrix4f invertedProjMat = new Matrix4f(projMat).invert();
+ Matrix4f invertedModelViewMat = new Matrix4f(stack.last().pose()).invert();
+ float minimumRadius = this.mc.gameRenderer.getRenderDistance();
+ for (PostPass pass : ((MixinPostChain)this.cloudShadows).simpleclouds$getPostPasses())
+ {
+ EffectInstance effect = pass.getEffect();
+ effect.setSampler("DepthSampler", () -> depthBufferId);
+ effect.safeGetUniform("InverseWorldProjMat").set(invertedProjMat);
+ effect.safeGetUniform("InverseModelViewMat").set(invertedModelViewMat);
+ effect.safeGetUniform("ShadowProjMat").set(this.shadowMap.get().getProjMatrix());
+ effect.safeGetUniform("ShadowModelViewMat").set(this.shadowMapStack.last().pose());
+ effect.safeGetUniform("CameraPos").set((float)camX, (float)camY, (float)camZ);
+ effect.safeGetUniform("MinimumRadius").set(minimumRadius);
+ }
+
+ this.cloudShadows.process(partialTick);
+
+ RenderSystem.depthMask(true);
+ }
+
+ public static void prepareShader(ShaderInstance shader, Matrix4f modelView, Matrix4f projMat, float fogStart, float fogEnd)
+ {
+ for (int i = 0; i < 12; ++i)
+ {
+ int j = RenderSystem.getShaderTexture(i);
+ shader.setSampler("Sampler" + i, j);
+ }
+
+ if (shader.MODEL_VIEW_MATRIX != null)
+ shader.MODEL_VIEW_MATRIX.set(modelView);
+
+ if (shader.PROJECTION_MATRIX != null)
+ shader.PROJECTION_MATRIX.set(projMat);
+
+ if (shader.INVERSE_VIEW_ROTATION_MATRIX != null)
+ shader.INVERSE_VIEW_ROTATION_MATRIX.set(RenderSystem.getInverseViewRotationMatrix());
+
+ if (shader.COLOR_MODULATOR != null)
+ shader.COLOR_MODULATOR.set(RenderSystem.getShaderColor());
+
+ if (shader.GLINT_ALPHA != null)
+ shader.GLINT_ALPHA.set(RenderSystem.getShaderGlintAlpha());
+
+ if (shader.FOG_START != null)
+ shader.FOG_START.set(fogStart);
+
+ if (shader.FOG_END != null)
+ shader.FOG_END.set(fogEnd);
+
+ if (shader.FOG_COLOR != null)
+ shader.FOG_COLOR.set(RenderSystem.getShaderFogColor());
+
+ if (shader.FOG_SHAPE != null)
+ shader.FOG_SHAPE.set(RenderSystem.getShaderFogShape().getIndex());
+
+ if (shader.TEXTURE_MATRIX != null)
+ shader.TEXTURE_MATRIX.set(RenderSystem.getTextureMatrix());
+
+ if (shader.GAME_TIME != null)
+ shader.GAME_TIME.set(RenderSystem.getShaderGameTime());
+
+ if (shader.SCREEN_SIZE != null)
+ {
+ Window window = Minecraft.getInstance().getWindow();
+ shader.SCREEN_SIZE.set((float) window.getWidth(), (float) window.getHeight());
+ }
+
+ shader.safeGetUniform("UseNormals").set(SimpleCloudsConfig.CLIENT.cubeNormals.get() ? 1 : 0);
+
+ RenderSystem.setShaderLights(DIFFUSE_LIGHT_0, DIFFUSE_LIGHT_1);
+ RenderSystem.setupShaderLights(shader);
+ }
+
+ public void copyDepthFromCloudsToMain()
+ {
+ this._copyDepthSafe(this.mc.getMainRenderTarget(), this.cloudTarget);
+ }
+
+ public void copyDepthFromMainToClouds()
+ {
+ this._copyDepthSafe(this.cloudTarget, this.mc.getMainRenderTarget());
+ }
+
+ public void copyDepthFromCloudsToTransparency()
+ {
+ this._copyDepthSafe(this.cloudTransparencyTarget, this.cloudTarget);
+ }
+
+ private void _copyDepthSafe(RenderTarget to, RenderTarget from)
+ {
+ RenderSystem.assertOnRenderThread();
+ GlStateManager._getError(); //Clear old error
+ if (!this.failedToCopyDepthBuffer)
+ {
+ to.bindWrite(false);
+ to.copyDepthFrom(from);
+ if (GlStateManager._getError() != GL11.GL_INVALID_OPERATION)
+ return;
+ boolean enabledStencil = false;
+ if (to.isStencilEnabled() && !from.isStencilEnabled())
+ {
+ from.enableStencil();
+ enabledStencil = true;
+ }
+ else if (from.isStencilEnabled() && !to.isStencilEnabled())
+ {
+ to.enableStencil();
+ enabledStencil = true;
+ }
+ if (enabledStencil)
+ {
+ to.copyDepthFrom(from);
+ if (GlStateManager._getError() == GL11.GL_INVALID_OPERATION)
+ {
+ LOGGER.error("Unable to copy depth between the main and clouds frame buffers, even after enabling stencil. Please note that the clouds may not render properly.");
+ this.failedToCopyDepthBuffer = true;
+ }
+ else
+ {
+ LOGGER.info("NOTE: Please ignore the above OpenGL error. Simple Clouds had to toggle stencil in order to copy the depth buffer between the main and clouds frame buffers.");
+ }
+ }
+ else
+ {
+ LOGGER.error("Unable to copy depth between the main and clouds frame buffers. Please note that the clouds may not render properly.");
+ this.failedToCopyDepthBuffer = true;
+ }
+ }
+ }
+
+ public void fillReport(CrashReport report)
+ {
+ CrashReportCategory category = report.addCategory("Simple Clouds Renderer");
+ category.setDetail("Cloud Mode", this.settings.getCurrentCloudMode());
+ category.setDetail("Cloud Target Available", this.cloudTarget != null);
+ category.setDetail("Storm Fog Target Active", this.stormFogTarget != null);
+ category.setDetail("Blur Target Active", this.blurTarget != null);
+ category.setDetail("Transparency Target Active", this.cloudTransparencyTarget != null);
+ category.setDetail("Post Chains", this.postChains.toString());
+ category.setDetail("Lightning Bolt SSBO", this.lightningBoltPositions);
+ category.setDetail("Clouds Shadow Map", this.stormFogShadowMap);
+ category.setDetail("Storm Fog Shadow Map", this.stormFogShadowMap);
+ category.setDetail("Failed to copy depth buffer", this.failedToCopyDepthBuffer);
+ category.setDetail("Needs Reload", this.needsReload);
+
+ CrashReportCategory meshGenCategory = report.addCategory("Cloud Mesh Generator");
+ if (this.meshGenerator != null)
+ {
+ meshGenCategory.setDetail("Type", this.meshGenerator.toString());
+ this.meshGenerator.fillReport(meshGenCategory);
+ }
+ else
+ {
+ meshGenCategory.setDetail("Type", "Mesh generator is not initialized");
+ }
+ }
+
+ public static void initialize(CloudsRendererSettings settings)
+ {
+ RenderSystem.assertOnRenderThread();
+ if (instance != null)
+ throw new IllegalStateException("Simple Clouds renderer is already initialized");
+ instance = new SimpleCloudsRenderer(settings, Minecraft.getInstance());
+ LOGGER.debug("Clouds render initialized");
+ }
+
+ public static SimpleCloudsRenderer getInstance()
+ {
+ return Objects.requireNonNull(instance, "Renderer not initialized!");
+ }
+
+ public static Optional getOptionalInstance()
+ {
+ return Optional.ofNullable(instance);
+ }
+
+ public static boolean canRenderInDimension(@Nullable ClientLevel level)
+ {
+ if (level == null)
+ return false;
+
+ List extends String> whitelist;
+ boolean useAsBlacklist;
+ if (ClientCloudManager.isAvailableServerSide() && SimpleCloudsConfig.SERVER_SPEC.isLoaded())
+ {
+ whitelist = SimpleCloudsConfig.SERVER.dimensionWhitelist.get();
+ useAsBlacklist = SimpleCloudsConfig.SERVER.whitelistAsBlacklist.get();
+ }
+ else
+ {
+ whitelist = SimpleCloudsConfig.CLIENT.dimensionWhitelist.get();
+ useAsBlacklist = SimpleCloudsConfig.CLIENT.whitelistAsBlacklist.get();
+ }
+
+ boolean flag = whitelist.stream().anyMatch(val -> {
+ return level.dimension().location().toString().equals(val);
+ });
+
+ return useAsBlacklist ? !flag : flag;
+ }
+
+ private static void renderChunkBox(VertexConsumer consumer, float minX, float minY, float minZ, float maxX, float maxY, float maxZ, float r, float g, float b, float a)
+ {
+ //-X
+ consumer.vertex(minX, minY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(minX, maxY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(minX, maxY, minZ).color(r, g, b, a).endVertex();
+ consumer.vertex(minX, minY, minZ).color(r, g, b, a).endVertex();
+
+ //+X
+ consumer.vertex(maxX, minY, minZ).color(r, g, b, a).endVertex();
+ consumer.vertex(maxX, maxY, minZ).color(r, g, b, a).endVertex();
+ consumer.vertex(maxX, maxY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(maxX, minY, maxZ).color(r, g, b, a).endVertex();
+
+ //-Y
+ consumer.vertex(maxX, minY, minZ).color(r, g, b, a).endVertex();
+ consumer.vertex(maxX, minY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(minX, minY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(minX, minY, minZ).color(r, g, b, a).endVertex();
+
+ //+Y
+ consumer.vertex(minX, maxY, minZ).color(r, g, b, a).endVertex();
+ consumer.vertex(minX, maxY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(maxX, maxY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(maxX, maxY, minZ).color(r, g, b, a).endVertex();
+
+ //-Z
+ consumer.vertex(minX, minY, minZ).color(r, g, b, a).endVertex();
+ consumer.vertex(minX, maxY, minZ).color(r, g, b, a).endVertex();
+ consumer.vertex(maxX, maxY, minZ).color(r, g, b, a).endVertex();
+ consumer.vertex(maxX, minY, minZ).color(r, g, b, a).endVertex();
+
+ //+Z
+ consumer.vertex(maxX, minY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(maxX, maxY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(minX, maxY, maxZ).color(r, g, b, a).endVertex();
+ consumer.vertex(minX, minY, maxZ).color(r, g, b, a).endVertex();
+ }
+
+ private static int calculateMeshGenInterval()
+ {
+ int fps = Minecraft.getInstance().getFps();
+ switch (SimpleCloudsConfig.CLIENT.generationInterval.get())
+ {
+ case STATIC:
+ {
+ return SimpleCloudsConfig.CLIENT.framesToGenerateMesh.get();
+ }
+ case DYNAMIC:
+ {
+ return Math.max(Mth.ceil((130.0F - (float)fps) / 30.0F) + 5, 1);
+ }
+ case TARGET_FPS:
+ {
+ return Math.max(Mth.ceil((float)fps / SimpleCloudsConfig.CLIENT.targetMeshGenFps.get()), 1);
+ }
+ default:
+ return 5;
+ }
+ }
+}
diff --git a/dev/nonamecrackers2/simpleclouds/client/renderer/pipeline/DefaultPipeline.java b/dev/nonamecrackers2/simpleclouds/client/renderer/pipeline/DefaultPipeline.java
new file mode 100644
index 00000000..9c89df20
--- /dev/null
+++ b/dev/nonamecrackers2/simpleclouds/client/renderer/pipeline/DefaultPipeline.java
@@ -0,0 +1,155 @@
+package dev.nonamecrackers2.simpleclouds.client.renderer.pipeline;
+
+import org.joml.Matrix4f;
+
+import com.mojang.blaze3d.pipeline.RenderTarget;
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexSorting;
+
+import dev.nonamecrackers2.simpleclouds.client.framebuffer.FrameBufferUtils;
+import dev.nonamecrackers2.simpleclouds.client.framebuffer.WeightedBlendingTarget;
+import dev.nonamecrackers2.simpleclouds.client.mesh.generator.CloudMeshGenerator;
+import dev.nonamecrackers2.simpleclouds.client.renderer.SimpleCloudsRenderer;
+import dev.nonamecrackers2.simpleclouds.client.world.FogRenderMode;
+import dev.nonamecrackers2.simpleclouds.common.config.SimpleCloudsConfig;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.culling.Frustum;
+import net.minecraft.util.profiling.ProfilerFiller;
+import net.minecraft.world.level.material.FogType;
+import nonamecrackers2.crackerslib.common.compat.CompatHelper;
+
+public class DefaultPipeline implements CloudsRenderPipeline
+{
+ protected DefaultPipeline() {}
+
+ @Override
+ public void prepare(Minecraft mc, SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ, Frustum frustum) {}
+
+ @Override
+ public void afterSky(Minecraft mc, SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ, Frustum frustum)
+ {
+ ProfilerFiller p = mc.getProfiler();
+
+ float[] cloudCol = renderer.getCloudColor(partialTick);
+ float cloudR = (float)cloudCol[0];
+ float cloudG = (float)cloudCol[1];
+ float cloudB = (float)cloudCol[2];
+
+ if (SimpleCloudsConfig.CLIENT.atmosphericClouds.get())
+ {
+ p.push("atmospheric_clouds");
+ renderer.getAtmosphericCloudRenderer().render(stack, projMat, partialTick, camX, camY, camZ, cloudR, cloudG, cloudB);
+ mc.getMainRenderTarget().bindWrite(false);
+ p.pop();
+ }
+
+ // Clouds
+
+ p.push("clouds");
+
+ stack.pushPose();
+
+ renderer.translateClouds(stack, camX, camY, camZ); // Prepare render for origin of camera
+
+ // Render opaque cloud geometry
+ p.push("clouds_opaque");
+
+ // Clears the cloud framebuffer and sets it as the current one
+ RenderTarget cloudTarget = renderer.getCloudTarget();
+ cloudTarget.clear(Minecraft.ON_OSX);
+ cloudTarget.bindWrite(false);
+
+ // Renders the clouds on to the cloud frame buffer
+ CloudMeshGenerator generator = renderer.getMeshGenerator();
+ SimpleCloudsRenderer.renderCloudsOpaque(generator, stack, projMat, renderer.getFogStart(), renderer.getFogEnd(), partialTick, cloudR, cloudG, cloudB, SimpleCloudsConfig.CLIENT.frustumCulling.get() ? frustum : null);
+
+ // Here we copy the depth from the cloud frame buffer to the main one, so we can have correct depth information with the
+ // rest of the Minecraft world
+ renderer.copyDepthFromCloudsToMain();
+
+ // Render transparent cloud geometry
+ p.popPush("clouds_transparent");
+
+ WeightedBlendingTarget transparencyTarget = renderer.getCloudTransparencyTarget();
+ transparencyTarget.clear(Minecraft.ON_OSX);
+
+ if (generator.transparencyEnabled())
+ {
+ // We use weighted order independent transparency as we cannot easily sort the cloud mesh
+ // More info here https://jcgt.org/published/0002/02/09/paper.pdf and http://casual-effects.blogspot.com/2015/03/implemented-weighted-blended-order.html
+ renderer.copyDepthFromCloudsToTransparency(); // Copy the depth data from the cloud framebuffer so we don't get weird depth issues
+ transparencyTarget.bindWrite(false);
+
+ // Render the transparent geometry to the transparency framebuffer
+ SimpleCloudsRenderer.renderCloudsTransparency(generator, stack, projMat, renderer.getFogStart(), renderer.getFogEnd(), partialTick, cloudR, cloudG, cloudB, SimpleCloudsConfig.CLIENT.frustumCulling.get() ? frustum : null);
+ }
+
+ p.pop();
+
+ stack.popPose();
+
+ // Render everything on to the main screen using a final composite shader
+ p.push("clouds_composite");
+ renderer.doFinalCompositePass(stack, partialTick, projMat);
+ p.pop();
+
+ p.pop();
+
+ // Storm Fog
+
+ if (SimpleCloudsConfig.CLIENT.renderStormFog.get())
+ {
+ p.push("storm_fog");
+
+ // Renders the storm fog at a lower resolution
+ renderer.doStormPostProcessing(stack, partialTick, projMat, camX, camY, camZ, cloudR, cloudG, cloudB);
+
+ // Next we blit the storm fog to a higher resolution texture and apply a box blur
+ RenderTarget target = renderer.getBlurTarget();
+ target.clear(Minecraft.ON_OSX); // Clear old contents on the blur framebuffer
+ target.bindWrite(true); // Bind write and resize viewport
+ // Here we blit the contents of the storm fog framebuffer on to the blur framebuffer. A special function is used here
+ // to preserve the alpha channel when rendering
+ FrameBufferUtils.blitTargetPreservingAlpha(renderer.getStormFogTarget(), mc.getWindow().getWidth(), mc.getWindow().getHeight());
+ // Blurs the storm fog
+ renderer.doBlurPostProcessing(partialTick);
+ // Renders the storm fog to the screen
+ mc.getMainRenderTarget().bindWrite(false);
+ RenderSystem.enableBlend();
+ RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ZERO, GlStateManager.DestFactor.ONE);
+ renderer.getBlurTarget().blitToScreen(mc.getWindow().getWidth(), mc.getWindow().getHeight(), false);
+ RenderSystem.disableBlend();
+ RenderSystem.defaultBlendFunc();
+ // Need to do this here because blitToScreen messes up the projection matrix and doesn't set it back
+ RenderSystem.setProjectionMatrix(projMat, VertexSorting.DISTANCE_TO_ORIGIN);
+
+ p.pop();
+ }
+
+ // Set the frame buffer back to the main one so everything else can render normally
+ mc.getMainRenderTarget().bindWrite(CompatHelper.isVrActive());
+ }
+
+ @Override
+ public void beforeWeather(Minecraft mc, SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ, Frustum frustum)
+ {
+ if (SimpleCloudsConfig.CLIENT.fogMode.get() == FogRenderMode.SCREEN_SPACE && mc.gameRenderer.getMainCamera().getFluidInCamera() == FogType.NONE)
+ {
+ renderer.doScreenSpaceWorldFog(stack, projMat, partialTick);
+ mc.getMainRenderTarget().bindWrite(false);
+ }
+ }
+
+ @Override
+ public void afterLevel(Minecraft mc, SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ, Frustum frustum)
+ {
+// mc.getProfiler().push("clouds_debug");
+// stack.pushPose();
+// renderer.translateClouds(stack, camX, camY, camZ);
+// SimpleCloudsRenderer.renderCloudsDebug(renderer.getMeshGenerator(), stack, projMat, partialTick, renderer.getFogStart(), renderer.getFogEnd(), frustum, false, true);
+// stack.popPose();
+// mc.getProfiler().pop();
+ }
+}
diff --git a/dev/nonamecrackers2/simpleclouds/client/renderer/pipeline/ShaderSupportPipeline.java b/dev/nonamecrackers2/simpleclouds/client/renderer/pipeline/ShaderSupportPipeline.java
new file mode 100644
index 00000000..5ea1293d
--- /dev/null
+++ b/dev/nonamecrackers2/simpleclouds/client/renderer/pipeline/ShaderSupportPipeline.java
@@ -0,0 +1,121 @@
+package dev.nonamecrackers2.simpleclouds.client.renderer.pipeline;
+
+import org.joml.Matrix4f;
+
+import com.mojang.blaze3d.pipeline.RenderTarget;
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexSorting;
+
+import dev.nonamecrackers2.simpleclouds.client.framebuffer.FrameBufferUtils;
+import dev.nonamecrackers2.simpleclouds.client.framebuffer.WeightedBlendingTarget;
+import dev.nonamecrackers2.simpleclouds.client.mesh.generator.CloudMeshGenerator;
+import dev.nonamecrackers2.simpleclouds.client.renderer.SimpleCloudsRenderer;
+import dev.nonamecrackers2.simpleclouds.common.config.SimpleCloudsConfig;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.culling.Frustum;
+import net.minecraft.util.profiling.ProfilerFiller;
+import nonamecrackers2.crackerslib.common.compat.CompatHelper;
+
+public class ShaderSupportPipeline implements CloudsRenderPipeline
+{
+ protected ShaderSupportPipeline() {}
+
+ @Override
+ public void prepare(Minecraft mc, SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ, Frustum frustum) {}
+
+ @Override
+ public void afterSky(Minecraft mc, SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ, Frustum frustum) {}
+
+ @Override
+ public void beforeWeather(Minecraft mc, SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ, Frustum frustum) {}
+
+ // To make Iris shaders work at a bare minimum, we render the clouds after the Iris render pipeline
+ @Override
+ public void afterLevel(Minecraft mc, SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, float partialTick, double camX, double camY, double camZ, Frustum frustum)
+ {
+ ProfilerFiller p = mc.getProfiler();
+
+ float[] cloudCol = renderer.getCloudColor(partialTick);
+ float cloudR = (float)cloudCol[0];
+ float cloudG = (float)cloudCol[1];
+ float cloudB = (float)cloudCol[2];
+
+ if (CompatHelper.areShadersRunning())
+ GlStateManager._depthMask(true);
+
+ // Render opaque cloud geometry
+ p.push("clouds_opaque");
+
+ stack.pushPose();
+
+ renderer.translateClouds(stack, camX, camY, camZ);
+
+ RenderTarget cloudTarget = renderer.getCloudTarget();
+ cloudTarget.clear(Minecraft.ON_OSX);
+ renderer.copyDepthFromMainToClouds(); // Copy depth from main framebuffer
+ cloudTarget.bindWrite(false);
+
+ // Renders the clouds on to the cloud frame buffer
+ CloudMeshGenerator generator = renderer.getMeshGenerator();
+ SimpleCloudsRenderer.renderCloudsOpaque(renderer.getMeshGenerator(), stack, projMat, renderer.getFogStart(), renderer.getFogEnd(), partialTick, cloudR, cloudG, cloudB, SimpleCloudsConfig.CLIENT.frustumCulling.get() ? frustum : null);
+
+ // Render transparent cloud geometry
+ p.popPush("clouds_transparent");
+
+ WeightedBlendingTarget transparencyTarget = renderer.getCloudTransparencyTarget();
+ transparencyTarget.clear(Minecraft.ON_OSX);
+
+ if (generator.transparencyEnabled())
+ {
+ // We use weighted order independent transparency as we cannot easily sort the cloud mesh
+ // More info here https://jcgt.org/published/0002/02/09/paper.pdf and http://casual-effects.blogspot.com/2015/03/implemented-weighted-blended-order.html
+ renderer.copyDepthFromCloudsToTransparency(); // Copy the depth data from the cloud framebuffer so we don't get weird depth issues
+ transparencyTarget.bindWrite(false);
+
+ // Render the transparent geometry to the transparency framebuffer
+ SimpleCloudsRenderer.renderCloudsTransparency(generator, stack, projMat, renderer.getFogStart(), renderer.getFogEnd(), partialTick, cloudR, cloudG, cloudB, SimpleCloudsConfig.CLIENT.frustumCulling.get() ? frustum : null);
+ }
+
+ p.pop();
+
+ stack.popPose();
+
+ // Render everything on to the main screen using a final composite shader
+ p.push("clouds_composite");
+ renderer.doFinalCompositePass(stack, partialTick, projMat);
+ p.pop();
+
+ mc.getMainRenderTarget().bindWrite(false);
+
+ if (SimpleCloudsConfig.CLIENT.renderStormFog.get())
+ {
+ p.push("storm_fog");
+
+ // Renders the storm fog at a lower resolution
+ renderer.doStormPostProcessing(stack, partialTick, projMat, camX, camY, camZ, cloudR, cloudG, cloudB);
+
+ // Next we blit the storm fog to a higher resolution texture and apply a box blur
+ RenderTarget target = renderer.getBlurTarget();
+ target.clear(Minecraft.ON_OSX); // Clear old contents on the blur framebuffer
+ target.bindWrite(true); // Bind write and resize viewport
+ // Here we blit the contents of the storm fog framebuffer on to the blur framebuffer. A special function is used here
+ // to preserve the alpha channel when rendering
+ FrameBufferUtils.blitTargetPreservingAlpha(renderer.getStormFogTarget(), mc.getWindow().getWidth(), mc.getWindow().getHeight());
+ // Blurs the storm fog
+ renderer.doBlurPostProcessing(partialTick);
+ // Renders the storm fog to the screen
+ mc.getMainRenderTarget().bindWrite(false);
+ RenderSystem.enableBlend();
+ RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ZERO, GlStateManager.DestFactor.ONE);
+ renderer.getBlurTarget().blitToScreen(mc.getWindow().getWidth(), mc.getWindow().getHeight(), false);
+ RenderSystem.disableBlend();
+ RenderSystem.defaultBlendFunc();
+ // Need to do this here because blitToScreen messes up the projection matrix and doesn't set it back
+ RenderSystem.setProjectionMatrix(projMat, VertexSorting.DISTANCE_TO_ORIGIN);
+
+ p.pop();
+ }
+ }
+}
diff --git a/dev/nonamecrackers2/simpleclouds/common/cloud/spawning/CloudGenerator.java b/dev/nonamecrackers2/simpleclouds/common/cloud/spawning/CloudGenerator.java
new file mode 100644
index 00000000..a65053c4
--- /dev/null
+++ b/dev/nonamecrackers2/simpleclouds/common/cloud/spawning/CloudGenerator.java
@@ -0,0 +1,413 @@
+package dev.nonamecrackers2.simpleclouds.common.cloud.spawning;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+import org.apache.commons.lang3.mutable.MutableObject;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.joml.Vector2f;
+import org.joml.Vector2i;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import dev.nonamecrackers2.simpleclouds.api.SimpleCloudsAPI;
+import dev.nonamecrackers2.simpleclouds.api.common.cloud.spawning.CreateRegionFunction;
+import dev.nonamecrackers2.simpleclouds.api.common.cloud.spawning.SpawnInfo;
+import dev.nonamecrackers2.simpleclouds.api.common.event.CloudRegionNaturallySpawnEvent;
+import dev.nonamecrackers2.simpleclouds.api.common.event.CloudRegionRemovedEvent;
+import dev.nonamecrackers2.simpleclouds.common.api.ScAPICloudGeneratorImplHelper;
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudType;
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudTypeSource;
+import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import dev.nonamecrackers2.simpleclouds.common.cloud.region.CloudRegion;
+import dev.nonamecrackers2.simpleclouds.common.world.SpawnRegion;
+import net.minecraft.util.Mth;
+import net.minecraft.util.RandomSource;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.Vec2;
+import net.minecraftforge.common.MinecraftForge;
+
+public abstract class CloudGenerator implements ScAPICloudGeneratorImplHelper
+{
+ private static final Logger LOGGER = LogManager.getLogger("simpleclouds/CloudGenerator");
+ private List spawnRegions = Lists.newArrayList();
+ private final List clouds = Lists.newArrayList();
+ protected RandomSource random = RandomSource.create();
+ protected final Supplier spawnConfig;
+ protected int ticksTillNextGen;
+ protected final CloudTypeSource cloudGetter;
+
+ public CloudGenerator(CloudTypeSource cloudGetter, Supplier spawnConfig)
+ {
+ this.cloudGetter = cloudGetter;
+ this.spawnConfig = spawnConfig;
+ }
+
+ public int getTicksTillNextGen()
+ {
+ return this.ticksTillNextGen;
+ }
+
+ public Supplier getSpawnConfig()
+ {
+ return this.spawnConfig;
+ }
+
+ @Override
+ public final List getClouds()
+ {
+ return ImmutableList.copyOf(this.clouds);
+ }
+
+ @Override
+ public final List getSpawnRegions()
+ {
+ return ImmutableList.copyOf(this.spawnRegions);
+ }
+
+ @Override
+ public List getCloudsInRegion(SpawnRegion region)
+ {
+ List clouds = Lists.newArrayList();
+ for (CloudRegion cloud : this.clouds)
+ {
+ if (cloud.intersects(region))
+ clouds.add(cloud);
+ }
+ return clouds;
+ }
+
+ @Override
+ public @Nullable CloudRegion getCloudAtWorldPosition(float worldX, float worldZ)
+ {
+ return this.getCloudAtPosition(worldX / (float)SimpleCloudsConstants.CLOUD_SCALE, worldZ / (float)SimpleCloudsConstants.CLOUD_SCALE);
+ }
+
+ @Override
+ public @Nullable CloudRegion getCloudAtPosition(float x, float z)
+ {
+ return CloudRegion.calculateAt(this.getClouds(), x, z).getLeft();
+ }
+
+ @Override
+ public List getRegionsThatOccupyCloud(CloudRegion cloud)
+ {
+ List regions = Lists.newArrayList();
+ for (SpawnRegion region : this.spawnRegions)
+ {
+ if (cloud.intersects(region))
+ regions.add(region);
+ }
+ return regions;
+ }
+
+ @Override
+ public final int getTotalCloudRegions()
+ {
+ return this.clouds.size();
+ }
+
+ @Override
+ public void setClouds(Collection clouds)
+ {
+ this.removeAllClouds();
+ clouds.forEach(r -> {
+ this.clouds.add(r);
+ });
+ }
+
+ @Override
+ public boolean removeAllClouds()
+ {
+ return this.removeClouds(r -> true);
+ }
+
+ @Override
+ public boolean removeClouds(Predicate predicate)
+ {
+ return this.removeCloudsCount(predicate) > 0;
+ }
+
+ @Override
+ public int removeCloudsCount(Predicate predicate)
+ {
+ int count = 0;
+ var iterator = this.clouds.iterator();
+ while (iterator.hasNext())
+ {
+ CloudRegion region = iterator.next();
+ if (predicate.test(region))
+ {
+ iterator.remove();
+ MinecraftForge.EVENT_BUS.post(new CloudRegionRemovedEvent(null, region, CloudRegionRemovedEvent.Reason.MANUALLY));
+ count++;
+ }
+ }
+ return count;
+ }
+
+ @Override
+ public boolean addCloud(CloudRegion region, CloudGenerator.Order order)
+ {
+ if (!this.cloudGetter.doesCloudTypeExist(region.getCloudTypeId()))
+ {
+ LOGGER.warn("Attempted to spawn a cloud formation: unknown id '{}'", region.getCloudTypeId());
+ return false;
+ }
+
+ if (this.clouds.contains(region))
+ return false;
+
+ // Ensures we wont go over the maximum cloud formations for all regions that would include
+ // this cloud formation
+ for (SpawnRegion spawnRegion : this.getRegionsThatOccupyCloud(region))
+ {
+ int totalCount = 0;
+ for (CloudRegion cloud : this.clouds)
+ {
+ if (cloud.intersects(spawnRegion))
+ totalCount++;
+ }
+ if (totalCount >= SimpleCloudsConstants.MAX_CLOUD_FORMATIONS)
+ {
+// System.out.println("refusing cloud region, too many");
+ return false;
+ }
+ }
+
+ order.appender.accept(this.clouds, region);
+
+// System.out.println(this.clouds.stream().map(CloudRegion::getOrderWeight).toList());
+
+ return true;
+ }
+
+ public void initialize(RandomSource random, Level level)
+ {
+ this.random = RandomSource.create();
+ this.spawnRegions = this.determineValidSpawnRegions(this.random, level);
+ this.removeAllClouds();
+ CloudSpawningConfig config = this.spawnConfig.get();
+ this.ticksTillNextGen = config.getSpawnInterval().sample(this.random);
+ }
+
+ public void tick(@Nullable Level level, float speed)
+ {
+ this.spawnRegions = this.determineValidSpawnRegions(this.random, level);
+
+ var iterator = this.clouds.iterator();
+ while (iterator.hasNext())
+ {
+ CloudRegion region = iterator.next();
+
+ //NOTE: If a cloud formation (region) is on the edge of a spawn region and is not visible, if the player moves even a slightly bit they can make that
+ //formation visible again causing it to tick. It could then move outside the region again, then the player can move and make it become
+ //visible again causing a cycle. This shouldn't happen as often since cloud formations shrink and will shrink extra fast when no longer visible,
+ //making it so they will shrink farther away from the edge of a spawn region preventing this, but it is behavior to note
+ boolean isVisible = SpawnRegion.doesCircleIntersect(this.spawnRegions, region.getWorldX(), region.getWorldZ(), region.getWorldRadius() / region.getStretch() + (float)SimpleCloudsConstants.CLOUD_SCALE / SimpleCloudsConstants.REGION_EDGE_FADE_FACTOR);
+ if (isVisible != region.wasPriorVisible())
+ this.onRegionVisibilityChange(region, isVisible);
+ region.tick(this.random, level, isVisible, speed);
+
+ if (!this.cloudGetter.doesCloudTypeExist(region.getCloudTypeId()))
+ {
+ LOGGER.warn("Cloud type with id {} no longer exists, removing cloud region", region.getCloudTypeId());
+ iterator.remove();
+ MinecraftForge.EVENT_BUS.post(new CloudRegionRemovedEvent(level, region, CloudRegionRemovedEvent.Reason.CLOUD_TYPE_NO_LONGER_EXISTS));
+ }
+
+ if (region.isDead())
+ {
+ iterator.remove();
+ CloudRegionRemovedEvent.Reason reason = CloudRegionRemovedEvent.Reason.NATURALLY;
+ if (!region.wasPriorVisible())
+ reason = CloudRegionRemovedEvent.Reason.NO_LONGER_VISIBLE;
+ MinecraftForge.EVENT_BUS.post(new CloudRegionRemovedEvent(level, region, reason));
+// if (level != null && !level.isClientSide)
+// System.out.println("cloud region died, was visible: " + isVisible + ", total: " + this.getTotalCloudRegions());
+ }
+ }
+
+ if (this.ticksTillNextGen > 0)
+ this.ticksTillNextGen -= Math.max(1, Mth.ceil(speed));
+
+ CloudSpawningConfig config = this.spawnConfig.get();
+
+ // In case the spawning config changes and the spawn interval is very different
+ int maxSpawnInterval = config.getSpawnInterval().getMaxValue();
+ if (this.ticksTillNextGen > maxSpawnInterval)
+ this.ticksTillNextGen = maxSpawnInterval;
+
+ if (!SimpleCloudsAPI.getApi().getHooks().isExternalWeatherControlEnabled())
+ {
+ if (!config.isEmpty() && this.shouldGenerateCloud(config, this.random, level))
+ this.spawnCloud(config, level);
+ }
+ }
+
+ protected boolean shouldGenerateCloud(CloudSpawningConfig config, RandomSource random, Level level)
+ {
+ return this.ticksTillNextGen <= 0;
+ }
+
+ public Optional spawnCloud(CloudSpawningConfig config, Level level)
+ {
+ return this.spawnCloud(() -> config.getRandom(this.random).orElse(null), config.getSpawnInterval().sample(this.random), config.getMaxRegions(), level);
+ }
+
+ @Override
+ public Optional spawnCloud(Supplier infoGetter, int nextSpawnInterval, int maxRegions, Level level)
+ {
+ return this.spawnCloud(infoGetter, nextSpawnInterval, maxRegions, level, this::createRegion);
+ }
+
+ @Override
+ public Optional spawnCloud(Supplier infoGetter, int nextSpawnInterval, int maxRegions, Level level, CreateRegionFunction regionFunc)
+ {
+ this.ticksTillNextGen = nextSpawnInterval;
+// System.out.println("next spawn attempt: " + this.ticksTillNextGen);
+
+ MutableObject spawnedCloud = new MutableObject<>();
+
+ SpawnRegion.randomPointForEachRegion(this.spawnRegions, this.random, SimpleCloudsConstants.SPAWN_ATTEMPTS, (r, p) ->
+ {
+ if (this.getCloudsInRegion(r).size() >= maxRegions)
+ return true;
+
+ float x = (float)p.x + 0.5F;
+ float z = (float)p.y + 0.5F;
+
+ SpawnInfo info = infoGetter.get();
+
+ if (info == null)
+ return false;
+
+ CloudType type = this.cloudGetter.getCloudTypeForId(info.cloudType());
+ if (type == null)
+ {
+ LOGGER.warn("Spawn config has unknown cloud type with id '{}'", info.cloudType());
+ return false;
+ }
+
+ return regionFunc.create(infoGetter.get(), (float)r.x() + 0.5F, (float)r.z() + 0.5F, x, z, this.random, true).map(apiRegion ->
+ {
+ CloudRegion region = (CloudRegion)apiRegion;
+ if (this.addCloud(region, CloudGenerator.Order.USE_WEIGHT))
+ {
+ spawnedCloud.setValue(region);
+ MinecraftForge.EVENT_BUS.post(new CloudRegionNaturallySpawnEvent(level, apiRegion));
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }).orElse(false);
+ });
+
+ return Optional.ofNullable(spawnedCloud.getValue());
+ }
+
+ @Override
+ public Optional createRegion(SpawnInfo info, float playerX, float playerZ, float x, float z, RandomSource random, boolean growTime)
+ {
+ for (CloudRegion region : this.getClouds())
+ {
+ float dist = Vector2f.distance(x, z, region.getWorldX(), region.getWorldZ()) - region.getWorldRadius();
+ if (dist <= SimpleCloudsConstants.MIN_SPAWN_DIST_BETWEEN_REGIONS)
+ return Optional.empty();
+ }
+
+ float deltaAdj = info.movesToPlayer() ? 0.1F : 1.0F;
+ float deltaX = (playerX - x) * (1.0F + random.nextFloat() * deltaAdj);
+ float deltaZ = (playerZ - z) * (1.0F + random.nextFloat() * deltaAdj);
+ float rotation = (float)Math.atan2(deltaX, deltaZ) + (float)Math.PI;
+ Vec2 direction;
+ if (random.nextInt(5) == 0)
+ direction = new Vec2(random.nextFloat() * 2.0F - 1.0F, random.nextFloat() * 2.0F - 1.0F).normalized();
+ else
+ direction = new Vec2(deltaX, deltaZ).normalized();
+
+ float radius = (float)info.determineRadius(random);
+ float maxSpeed = info.determineSpeed(random);
+ float accelerationFactor = 0.01F;
+ int existTicks = info.determineExistTicks(random);
+ int growTicks = growTime ? info.determineGrowTicks(random) : 0;
+ float stretchFactor = info.determineStretchFactor(random);
+
+ return Optional.of(new CloudRegion(info.cloudType(), direction, maxSpeed, accelerationFactor, x / (float)SimpleCloudsConstants.CLOUD_SCALE, z / (float)SimpleCloudsConstants.CLOUD_SCALE, radius / (float)SimpleCloudsConstants.CLOUD_SCALE, rotation, stretchFactor, existTicks, growTicks, info.orderWeight()));
+ }
+
+ public void doInitialGen(int x, int z, Level level, boolean ignoreOtherRegions)
+ {
+ SpawnRegion region = new SpawnRegion(x, z, SimpleCloudsConstants.SPAWN_RADIUS);
+
+ CloudSpawningConfig config = this.spawnConfig.get();
+
+ if (this.getCloudsInRegion(region).size() > config.getMaxInitialRegions())
+ return;
+
+ for (int i = 0; i < config.getMaxInitialRegions(); i++)
+ {
+ for (int j = 0; j < SimpleCloudsConstants.SPAWN_ATTEMPTS; j++)
+ {
+ Vector2i pos = SpawnRegion.getRandomPointInRegion(region, this.random);
+ if (this.getCloudsInRegion(region).size() >= config.getMaxInitialRegions())
+ continue;
+ if (!ignoreOtherRegions && this.spawnRegions.stream().anyMatch(r -> r.includesPoint(pos.x, pos.y)))
+ continue;
+ CloudRegion cloudFormation = this.createRegion(config.getRandom(this.random).orElse(null), (float)x + 0.5F, (float)z + 0.5F, (float)pos.x + 0.5F, (float)pos.y + 0.5F, this.random, false).orElse(null);
+ if (cloudFormation == null)
+ continue;
+ this.addCloud(cloudFormation, CloudGenerator.Order.USE_WEIGHT);
+ break;
+ }
+ }
+ }
+
+ protected void onRegionVisibilityChange(CloudRegion region, boolean nowVisible) {}
+
+ protected abstract List determineValidSpawnRegions(RandomSource random, @Nullable Level level);
+
+ public static enum Order
+ {
+ TOP((l, r) ->
+ {
+ l.add(r);
+ }),
+ BOTTOM((l, r) ->
+ {
+ l.add(0, r);
+ }),
+ USE_WEIGHT((l, r) ->
+ {
+ int prevWeight = 0;
+ for (int i = 0; i < l.size(); i++)
+ {
+ CloudRegion region = l.get(i);
+ if (r.getOrderWeight() >= prevWeight && r.getOrderWeight() <= region.getOrderWeight())
+ {
+ l.add(i, r);
+ prevWeight = region.getOrderWeight();
+ return;
+ }
+ }
+ l.add(r);
+ });
+
+ private final BiConsumer, CloudRegion> appender;
+
+ private Order(BiConsumer, CloudRegion> appender)
+ {
+ this.appender = appender;
+ }
+ }
+}
diff --git a/dev/nonamecrackers2/simpleclouds/common/world/CloudManager.java b/dev/nonamecrackers2/simpleclouds/common/world/CloudManager.java
new file mode 100644
index 00000000..f7c74bb7
--- /dev/null
+++ b/dev/nonamecrackers2/simpleclouds/common/world/CloudManager.java
@@ -0,0 +1,427 @@
+package dev.nonamecrackers2.simpleclouds.common.world;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BiFunction;
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+import dev.nonamecrackers2.simpleclouds.common.api.SimpleCloudsHooks;
+import org.apache.commons.lang3.tuple.Pair;
+import org.joml.Vector2f;
+
+import dev.nonamecrackers2.simpleclouds.api.SimpleCloudsAPI;
+import dev.nonamecrackers2.simpleclouds.api.common.cloud.CloudMode;
+import dev.nonamecrackers2.simpleclouds.api.common.cloud.weather.WeatherType;
+import dev.nonamecrackers2.simpleclouds.api.common.event.ModifyCloudSpeedEvent;
+import dev.nonamecrackers2.simpleclouds.api.common.world.ScAPICloudManager;
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudType;
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudTypeSource;
+import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import dev.nonamecrackers2.simpleclouds.common.cloud.region.CloudGetter;
+import dev.nonamecrackers2.simpleclouds.common.cloud.region.CloudRegion;
+import dev.nonamecrackers2.simpleclouds.common.cloud.spawning.CloudGenerator;
+import dev.nonamecrackers2.simpleclouds.common.cloud.spawning.CloudSpawningConfig;
+import dev.nonamecrackers2.simpleclouds.common.config.SimpleCloudsConfig;
+import net.minecraft.core.BlockPos;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.dedicated.DedicatedServer;
+import net.minecraft.util.Mth;
+import net.minecraft.util.RandomSource;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.level.levelgen.Heightmap;
+import net.minecraftforge.common.MinecraftForge;
+
+public abstract class CloudManager implements CloudGetter, ScAPICloudManager
+{
+ public static final int CLOUD_HEIGHT_MAX = 2048;
+ public static final int CLOUD_HEIGHT_MIN = 0;
+ public static final int UPDATE_INTERVAL = 200;
+ public static final float RANDOM_SPREAD = 10000.0F;
+ public static final float SCROLL_OFFSET = 100.0F;
+ protected final T level;
+ protected final CloudTypeSource cloudSource;
+ protected final CloudGenerator cloudGenerator;
+ private long seed;
+ protected @Nullable RandomSource random;
+ protected float scrollAngle;
+ protected float scrollXO;
+ protected float scrollYO;
+ protected float scrollZO;
+ protected float scrollX;
+ protected float scrollY;
+ protected float scrollZ;
+ protected float speed = 1.0F;
+ protected int cloudHeight = 128;
+ protected int tickCount;
+ protected int nextLightningStrike = 60;
+ protected boolean useVanillaWeather;
+
+ @SuppressWarnings("unchecked")
+ public static CloudManager get(T level)
+ {
+ return Objects.requireNonNull(((CloudManagerHolder)level).getCloudManager(), "Cloud manager is not available, this shouldn't happen!");
+ }
+
+ public CloudManager(T level, CloudTypeSource source, Supplier configGetter, BiFunction, CloudGenerator> generatorFunc)
+ {
+ this.level = level;
+ this.cloudSource = source;
+ this.cloudGenerator = generatorFunc.apply(this, configGetter);
+ this.useVanillaWeather = this.determineUseVanillaWeather();
+ }
+
+ @Override
+ public CloudGenerator getCloudGenerator()
+ {
+ return this.cloudGenerator;
+ }
+
+ @Override
+ public List getClouds()
+ {
+ return this.cloudGenerator.getClouds();
+ }
+
+ @Override
+ public CloudType getCloudTypeForId(ResourceLocation id)
+ {
+ return this.cloudSource.getCloudTypeForId(id);
+ }
+
+ @Override
+ public CloudType[] getIndexedCloudTypes()
+ {
+ return this.cloudSource.getIndexedCloudTypes();
+ }
+
+ @Override
+ public boolean isCloudGeneratorActive() {
+ return this.getCloudMode() != CloudMode.SINGLE;
+ }
+
+
+ public void onPlayerJoin(Player player)
+ {
+ if (this.isCloudGeneratorActive() && !SimpleCloudsAPI.getApi().getHooks().isExternalWeatherControlEnabled())
+ this.cloudGenerator.doInitialGen(player.getBlockX(), player.getBlockZ(), this.level, false);
+ }
+
+ @Override
+ public Pair getCloudTypeAtPosition(float x, float z)
+ {
+ if (this.getCloudMode() != CloudMode.SINGLE)
+ {
+ Pair result = CloudRegion.calculateAt(this.getClouds(), x, z);
+ CloudType type = null;
+ if (result.getLeft() != null)
+ type = this.getCloudTypeForId(result.getLeft().getCloudTypeId());
+ if (type == null)
+ type = SimpleCloudsConstants.EMPTY;
+ return Pair.of(type, 1.0F - result.getRight());
+ }
+ else
+ {
+ String rawId = this.getSingleModeCloudTypeRawId();
+ ResourceLocation id = ResourceLocation.tryParse(rawId);
+ if (id != null)
+ {
+ CloudType type = this.getCloudTypeForId(id);
+ if (type != null)
+ return Pair.of(type, 0.0F);
+ }
+ return Pair.of(SimpleCloudsConstants.EMPTY, 0.0F);
+ }
+ }
+
+ public Pair getPrecipitationAt(BlockPos pos)
+ {
+ if (!this.level.canSeeSky(pos) || this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos).getY() > pos.getY())
+ return Pair.of(false, Biome.Precipitation.NONE);
+
+ Biome.Precipitation precipitation = this.level.getBiome(pos).value().getPrecipitationAt(pos);
+
+ var info = this.getCloudTypeAtWorldPos((float)pos.getX() + 0.5F, (float)pos.getZ() + 0.5F);
+ CloudType type = info.getLeft();
+ if ((float)pos.getY() + 0.5F > type.stormStart() * SimpleCloudsConstants.CLOUD_SCALE + 128.0F)
+ return Pair.of(false, Biome.Precipitation.NONE);
+
+ if (info.getLeft().weatherType().includesRain() && info.getRight() < SimpleCloudsConstants.RAIN_THRESHOLD - 0.01F)
+ return Pair.of(true, precipitation);
+ else
+ return Pair.of(false, Biome.Precipitation.NONE);
+ }
+
+ //For API calls, use Level#isRainingAt
+ public boolean isRainingAt(BlockPos pos)
+ {
+ Pair val = this.getPrecipitationAt(pos);
+ return val.getLeft() && val.getRight() != Biome.Precipitation.RAIN;
+ }
+
+ public boolean isSnowingAt(BlockPos pos)
+ {
+ Pair val = this.getPrecipitationAt(pos);
+ return val.getLeft() && val.getRight() == Biome.Precipitation.SNOW;
+ }
+
+ public boolean hasPrecipitationAt(BlockPos pos)
+ {
+ Pair val = this.getPrecipitationAt(pos);
+ return val.getLeft() && val.getRight() != Biome.Precipitation.NONE;
+ }
+
+ @Override
+ public float getRainLevel(float x, float y, float z)
+ {
+ var info = this.getCloudTypeAtWorldPos(x, z);
+ CloudType type = info.getLeft();
+
+ if (!type.weatherType().includesRain())
+ return 0.0F;
+
+ float fade = info.getRight();
+ float verticalFade = 1.0F - Mth.clamp((y - (type.stormStart() * SimpleCloudsConstants.CLOUD_SCALE + this.getCloudHeight())) / SimpleCloudsConstants.RAIN_VERTICAL_FADE, 0.0F, 1.0F);
+ return Math.min(1.0F, Math.max(0.0F, SimpleCloudsConstants.RAIN_THRESHOLD - fade) / SimpleCloudsConstants.RAIN_FADE) * verticalFade;
+ }
+
+ public void init(long seed)
+ {
+ RandomSource random = this.setSeed(seed);
+ this.random = random;
+ this.speed = 1.0F;
+ this.cloudGenerator.initialize(random, this.level);
+ }
+
+ @Override
+ public int getCloudHeight()
+ {
+ return this.cloudHeight;
+ }
+
+ @Override
+ public void setCloudHeight(int height)
+ {
+ this.cloudHeight = height;
+ }
+
+ public void tick()
+ {
+ MinecraftServer server = this.level.getServer();
+ if (server instanceof DedicatedServer && server.getPlayerCount() == 0)
+ return;
+
+ this.tickCount++;
+
+ this.scrollXO = this.scrollX;
+ this.scrollYO = this.scrollY;
+ this.scrollZO = this.scrollZ;
+ float speed = this.getCloudSpeed();
+ speed = this.modifyCloudSpeed(speed);
+
+ if (this.isCloudGeneratorActive())
+ this.cloudGenerator.tick(this.level, speed);
+
+ speed *= 0.0001F;
+ this.scrollAngle += speed;
+ this.scrollX = (float)Math.cos(this.scrollAngle) * SCROLL_OFFSET;
+ this.scrollY = 0.0F;//(float)Math.sin(this.scrollAngle + (float)Math.PI / 4.0F) * SCROLL_OFFSET * 0.5F;
+ this.scrollZ = (float)Math.sin(this.scrollAngle) * SCROLL_OFFSET;
+
+ boolean flag = this.determineUseVanillaWeather();
+ if (flag != this.useVanillaWeather)
+ {
+ this.useVanillaWeather = flag;
+ this.resetVanillaWeather();
+ }
+
+ if (!this.useVanillaWeather)
+ this.tickLightning();
+ }
+
+ protected void resetVanillaWeather() {}
+
+ protected void tickLightning()
+ {
+ if (this.nextLightningStrike <= 0 || --this.nextLightningStrike > 0)
+ return;
+ this.attemptToSpawnLightning();
+ int minInterval = SimpleCloudsConfig.COMMON.lightningSpawnIntervalMin.get();
+ int maxInterval = Math.max(minInterval, SimpleCloudsConfig.COMMON.lightningSpawnIntervalMax.get());
+ this.nextLightningStrike = Mth.randomBetweenInclusive(this.random, minInterval, maxInterval);
+ }
+
+ protected boolean determineUseVanillaWeather()
+ {
+ return useVanillaWeather(this.level, this);
+ }
+
+ @Override
+ public final boolean shouldUseVanillaWeather()
+ {
+ return this.useVanillaWeather;
+ }
+
+ protected abstract void attemptToSpawnLightning();
+
+ protected abstract void spawnLightning(CloudType type, float fade, int x, int z, boolean soundOnly);
+
+ @Override
+ public abstract CloudMode getCloudMode();
+
+ @Override
+ public abstract String getSingleModeCloudTypeRawId();
+
+ @Override
+ public void spawnLightning(int x, int z, boolean soundOnly)
+ {
+ var info = this.getCloudTypeAtWorldPos((float)x + 0.5F, (float)z + 0.5f);
+ this.spawnLightning(info.getLeft(), info.getRight(), x, z, soundOnly);
+ }
+
+ @Override
+ public Vector2f calculateWindDirection()
+ {
+ float dirX = Mth.cos(this.scrollAngle);
+ float dirZ = Mth.sin(this.scrollAngle);
+ return new Vector2f(dirX, dirZ);
+ }
+
+ @Override
+ public int getTickCount()
+ {
+ return this.tickCount;
+ }
+
+ @Override
+ public long getSeed()
+ {
+ return this.seed;
+ }
+
+ public RandomSource setSeed(long seed)
+ {
+ this.seed = seed;
+ return RandomSource.create(seed);
+ }
+
+ protected float modifyCloudSpeed(float speed)
+ {
+ ModifyCloudSpeedEvent event = new ModifyCloudSpeedEvent(this.level, this, speed);
+ MinecraftForge.EVENT_BUS.post(event);
+ return event.getCurrentSpeed();
+ }
+
+ @Override
+ public float getCloudSpeed()
+ {
+ return this.speed;
+ }
+
+ @Override
+ public void setCloudSpeed(float speed)
+ {
+ this.speed = Math.max(0.0F, speed);
+ }
+
+ @Override
+ public float getScrollAngle()
+ {
+ return this.scrollAngle;
+ }
+
+ @Override
+ public void setScrollAngle(float angle)
+ {
+ this.scrollAngle = angle;
+ }
+
+ @Override
+ public float getScrollX()
+ {
+ return this.scrollX;
+ }
+
+ @Override
+ public float getScrollY()
+ {
+ return this.scrollY;
+ }
+
+ @Override
+ public float getScrollZ()
+ {
+ return this.scrollZ;
+ }
+
+ @Override
+ public float getScrollX(float partialTicks)
+ {
+ return Mth.lerp(partialTicks, this.scrollXO, this.scrollX);
+ }
+
+ @Override
+ public float getScrollY(float partialTicks)
+ {
+ return Mth.lerp(partialTicks, this.scrollYO, this.scrollY);
+ }
+
+ @Override
+ public float getScrollZ(float partialTicks)
+ {
+ return Mth.lerp(partialTicks, this.scrollZO, this.scrollZ);
+ }
+
+ public static boolean isValidLightning(CloudType type, float fade, RandomSource random)
+ {
+ return type.weatherType().includesThunder() && fade < 0.8F;// && (fade > 0.7F || random.nextInt(3) == 0);
+ }
+
+ public static boolean useVanillaWeather(Level level, CloudTypeSource source)
+ {
+ if (!SimpleCloudsConfig.SERVER_SPEC.isLoaded())
+ return false;
+
+ boolean flag = SimpleCloudsConfig.SERVER.dimensionWhitelist.get().stream().anyMatch(val -> {
+ return level.dimension().location().toString().equals(val);
+ });
+
+ if (SimpleCloudsConfig.SERVER.whitelistAsBlacklist.get() ? flag : !flag)
+ return true;
+
+ CloudMode mode = SimpleCloudsConfig.SERVER.cloudMode.get();
+
+ switch (mode)
+ {
+ case AMBIENT:
+ {
+ return true;
+ }
+ case SINGLE:
+ {
+ String rawId = SimpleCloudsConfig.SERVER.singleModeCloudType.get();
+ ResourceLocation id = ResourceLocation.tryParse(rawId);
+ if (id != null)
+ {
+ CloudType type = source.getCloudTypeForId(id);
+ if (type != null && type.weatherType() == WeatherType.NONE)
+ return true;
+ }
+ }
+ default:
+ {
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.getClass().getSimpleName() + "[level=" + this.level.dimension().location() + "]";
+ }
+}
diff --git a/docs/mods/projectatmosphere/faq.md b/docs/mods/projectatmosphere/faq.md
new file mode 100644
index 00000000..711c40ac
--- /dev/null
+++ b/docs/mods/projectatmosphere/faq.md
@@ -0,0 +1,34 @@
+# FAQ
+
+## Which Minecraft version and loader are supported?
+Repository metadata currently targets Minecraft `1.20.1` on Forge.
+
+## Does Project Atmosphere require Simple Clouds?
+Yes. `simpleclouds` is a mandatory dependency in `mods.toml`.
+
+## Does it require Serene Seasons?
+Official docs describe Serene Seasons support, but current code actually requires some season provider at startup. The code names Serene Seasons, PA x TFC, and Ecliptic Seasons as accepted providers. Official support for the non-Serene options still needs confirmation.
+
+## Does `0.8.0.0` work with PA x TFC?
+Not officially. The `0.8.0.0` changelog says it is temporarily incompatible.
+
+## Does it support Dynamic Trees?
+There is integration code, but the latest official note says the module is still work in progress and should always be disabled.
+
+## How do I inspect current weather data?
+Use the `/pa` commands. The most useful support commands are:
+- `/pa temperature forecast`
+- `/pa humidity get`
+- `/pa pressure get`
+- `/pa windSpeed get`
+- `/pa weatherdebug forecast`
+- `/pa weatherdebug fog`
+
+## How do I force fog for testing?
+In the current source tree, use `/pa fog spawn [strength] [seconds]` and `/pa fog clear`.
+
+## How do I override biome temperatures?
+Edit `config/projectatmosphere/biome_temps.json`. You can set either one `all` range for a biome or explicit `winter`, `spring`, `summer`, and `autumn` min/max ranges.
+
+## Does it support Fabric or NeoForge?
+No official support for Fabric or NeoForge is documented in this bundle.
diff --git a/docs/mods/projectatmosphere/install.md b/docs/mods/projectatmosphere/install.md
new file mode 100644
index 00000000..5f38de53
--- /dev/null
+++ b/docs/mods/projectatmosphere/install.md
@@ -0,0 +1,34 @@
+# Install Project Atmosphere
+
+## Prerequisites
+- Minecraft `1.20.1`
+- Forge `47+`
+- Project Atmosphere `0.8.0.0`
+
+## Required mods
+- Project Atmosphere
+- Simple Clouds
+- Gabou's Libs
+
+## Season provider note
+Official documentation explicitly mentions Serene Seasons support. Current code also refuses to start without a season provider and names these accepted providers in the startup check:
+- `sereneseasons`
+- `projectatmospherefortfc`
+- `eclipticseasons`
+
+Official support for the non-Serene options still needs maintainer confirmation, so treat them as observed runtime behavior, not confirmed public compatibility.
+
+## Installation order
+1. Install Forge for Minecraft `1.20.1`.
+2. Add the required dependency mods.
+3. Add a season provider.
+4. Add Project Atmosphere.
+5. Add optional compatibility mods only after the core setup works.
+
+No special jar ordering is required once the correct files are present in the `mods` folder.
+
+## Basic verification
+1. Start the game or server and confirm it reaches the main menu/world load without dependency errors.
+2. Check that no startup error mentions missing `simpleclouds`, `gaboulibs`, or a missing season provider.
+3. In an Overworld test world, run `/pa temperature forecast`.
+4. If you want to test current-source fog commands, run `/pa fog spawn`.
diff --git a/docs/mods/projectatmosphere/overview.md b/docs/mods/projectatmosphere/overview.md
new file mode 100644
index 00000000..265ac2d6
--- /dev/null
+++ b/docs/mods/projectatmosphere/overview.md
@@ -0,0 +1,30 @@
+# Project Atmosphere overview
+
+## What it does
+Project Atmosphere is a Minecraft Forge 1.20.1 weather and climate mod. It simulates regional temperature, humidity, pressure, wind, and cloud-driven weather instead of relying only on vanilla weather flags.
+
+## Main features
+- Season-aware temperature forecasting by biome/region.
+- Regional humidity, pressure, and wind sampling.
+- Simple Clouds integration for cloud-driven weather visuals.
+- Weather world effects such as rain fire suppression and cauldron filling.
+- Admin/debug commands for forecast inspection and weather testing.
+- Optional biome temperature overrides through `config/projectatmosphere/biome_temps.json`.
+
+## Current source features
+The current source tree also contains severe-weather and fog systems such as tornadoes, hurricanes, and dynamic fog. Their exact release/support status for `0.8.0.0` is not fully documented in official release notes, so treat them as current-source behavior rather than fully documented release guarantees.
+
+## Who it is for
+- Players using climate or realism-focused Forge 1.20.1 packs.
+- Pack/server admins who want weather diagnostics and configurable climate behavior.
+- Mod integrators who want region-based weather data through the public API.
+
+## High-level dependencies
+- Required by `mods.toml`: Forge, Minecraft, Simple Clouds, Gabou's Libs.
+- Officially documented optional integrations: Serene Seasons, Serene Seasons Extended, Pretty Rain.
+- Observed current runtime note: the code will abort startup if no season provider is present. See the install and troubleshooting guides for details.
+
+## Important compatibility notes
+- Official latest note: `0.8.0.0` is temporarily incompatible with PA x TFC.
+- Official latest note: Dynamic Trees integration remains work in progress and should stay disabled.
+- Official loader support in repository metadata is Forge only.
diff --git a/docs/mods/projectatmosphere/troubleshooting.md b/docs/mods/projectatmosphere/troubleshooting.md
new file mode 100644
index 00000000..396a4338
--- /dev/null
+++ b/docs/mods/projectatmosphere/troubleshooting.md
@@ -0,0 +1,46 @@
+# Troubleshooting
+
+## Startup or mod loading failure
+
+### Wrong game version or loader
+Project Atmosphere is currently documented for Minecraft `1.20.1` on Forge. Using Fabric, NeoForge, or another Minecraft version is not confirmed here.
+
+### Missing required dependency
+If the log mentions `simpleclouds` or `gaboulibs`, install those mods first. They are declared as mandatory in `mods.toml`.
+
+### Missing season provider
+Current code throws an error if no season provider is loaded. If startup fails with a message about installing Serene Seasons, ProjectAtmosphereForTFC, or Ecliptic Seasons, add one of those mods and retry.
+
+### PA x TFC incompatibility on 0.8.0.0
+The official `0.8.0.0` changelog marks PA x TFC as temporarily incompatible. If that bridge is installed and weather logic behaves incorrectly, remove it or wait for a compatibility update.
+
+## Commands do not work
+
+### Use the `/pa` root
+Current server commands are registered under `/pa`. If older docs mention `/weatherdebug`, use `/pa weatherdebug` instead.
+
+### Wrong dimension
+Many forecast and debug commands only work in the Overworld.
+
+### Missing permission level
+Admin/debug commands such as cloud spawning, tornado spawning, hurricane spawning, and fog forcing require permission level `2`.
+
+## Config problems
+
+### Common config values
+Use the mod's Forge common config entries for weather tuning. The schema in `data/mcp/mods/projectatmosphere/config-schema.json` maps the real keys and default values.
+
+### Biome temperature overrides
+Custom biome temperature overrides live in `config/projectatmosphere/biome_temps.json`. Invalid biome ids or malformed season ranges are skipped instead of applied.
+
+### Dynamic Trees integration
+Latest official notes say the Dynamic Trees module is still work in progress and should remain disabled.
+
+## Known command issue
+`/pa weatherdebug snowstorm` is currently mis-registered in the source tree: the handler expects an `intensity` argument that the command does not expose. Treat that command as broken until fixed.
+
+## When to check known issues
+Check `data/mcp/mods/projectatmosphere/known-issues.json` when:
+- a compatibility problem appears after updating to `0.8.0.0`
+- an admin/debug command behaves inconsistently
+- an integration is mentioned in old docs but not in current release notes
diff --git a/gradle.properties b/gradle.properties
index 06e132e5..694f7915 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,7 @@ mixin.gradle.disableDiffplug=true
# Minecraft + Forge versions
minecraft_version=1.20.1
minecraft_version_range=[1.20.1,1.21)
-forge_version=47.4.13
+forge_version=47.4.20
forge_version_range=[47,)
loader_version_range=[47,)
@@ -19,7 +19,7 @@ mod_id=projectatmosphere
mod_name=Project Atmosphere
mod_group_id=net.Gabou.projectatmosphere
mod_license=All Rights Reserved
-mod_version=0.8.0.0
+mod_version=0.9.0.0
mod_authors=Gabou
mod_description=Dynamic seasonal temperature and weather simulation with support for Serene Seasons and shaders.
mod_mixins=projectatmosphere.mixins.json
diff --git a/libs/simpleclouds-0.7.4+1.20.1-forge-all.jar b/libs/simpleclouds-0.7.4+1.20.1-forge-all.jar
new file mode 100644
index 00000000..bc8addb6
Binary files /dev/null and b/libs/simpleclouds-0.7.4+1.20.1-forge-all.jar differ
diff --git a/libs/simpleclouds-0.7.4+1.20.1-forge-sources.jar b/libs/simpleclouds-0.7.4+1.20.1-forge-sources.jar
new file mode 100644
index 00000000..15af3950
Binary files /dev/null and b/libs/simpleclouds-0.7.4+1.20.1-forge-sources.jar differ
diff --git a/pmweather_tornado_extraction_export.zip b/pmweather_tornado_extraction_export.zip
new file mode 100644
index 00000000..38b29bb8
Binary files /dev/null and b/pmweather_tornado_extraction_export.zip differ
diff --git a/pmweather_tornado_extraction_export/README.md b/pmweather_tornado_extraction_export/README.md
new file mode 100644
index 00000000..5ff2ed15
--- /dev/null
+++ b/pmweather_tornado_extraction_export/README.md
@@ -0,0 +1,111 @@
+# PMWeather Tornado Extraction
+
+This folder contains the PMWeather tornado code path that matters most if you want to port the behavior into another mod.
+
+Important: PMWeather does not render the tornado as a normal model or mesh.
+
+- The close-range visual motion comes from particles and debris being advected by the tornado wind field.
+- The large visible funnel and wall cloud are generated in the volumetric cloud shader.
+- The tornado backend is mainly `Storm.java` plus `WindEngine.java`.
+
+## Core Backend
+
+- `dev/protomanly/pmweather/weather/Storm.java`
+ - Storm lifecycle, widening, intensification, dying, chunk force-loading, pulling entities/particles, block damage, debris spawning.
+ - Key methods:
+ - `tick()`
+ - `doDamage(...)`
+ - `getRankine(...)`
+ - `getWind(...)`
+ - `pull(Particle, ...)`
+ - `pull(Entity, ...)`
+- `dev/protomanly/pmweather/weather/Vorticy.java`
+ - Secondary subvortices orbiting the tornado.
+- `dev/protomanly/pmweather/weather/WindEngine.java`
+ - Blends ambient wind, storm inflow/rotation, and the tornado-specific wind field.
+ - This is what drives particles and debris into a funnel-looking motion.
+- `dev/protomanly/pmweather/weather/WeatherHandler.java`
+- `dev/protomanly/pmweather/weather/WeatherHandlerClient.java`
+ - Storm management and client sync for debris particles.
+
+## Visual Funnel
+
+- `dev/protomanly/pmweather/shaders/ModShaders.java`
+ - Sends storm uniforms to the volumetric shader:
+ - position
+ - width
+ - windspeed
+ - touchdown speed
+ - random tornado shape
+ - spin
+ - occlusion
+- `assets/pmweather/shaders/program/clouds.fsh`
+ - This is the actual funnel shape logic.
+ - Search these sections:
+ - `tornadic`
+ - `torPerc`
+ - `tornadoHeight`
+ - `torShape`
+ - `ropeMod`
+ - `dust`
+- `assets/pmweather/shaders/program/clouds.json`
+- `assets/pmweather/shaders/post/clouds.json`
+- `dev/protomanly/pmweather/render/RenderEvents.java`
+ - Hooks the shader render pass into the level render.
+- `dev/protomanly/pmweather/event/ModBusClientEvents.java`
+ - Reload listener and client-side registration that the shader/debris path depends on.
+- `dev/protomanly/pmweather/mixin/PostChainMixin.java`
+ - Accessor mixin used by `ModShaders` to reach the post-pass list.
+- `dev/protomanly/pmweather/interfaces/PostChainData.java`
+- `dev/protomanly/pmweather/compat/DistantHorizons.java`
+- `dev/protomanly/pmweather/compat/DistantHorizonsHandler.java`
+ - Optional compatibility layer used by the shader when Distant Horizons is present.
+- `pmweather.mixins.json`
+ - Needed if you want the `PostChainMixin` path to work as PMWeather does it.
+
+## Particle And Debris Layer
+
+- `dev/protomanly/pmweather/event/GameBusClientEvents.java`
+ - Ticks custom particle managers and applies `WindEngine.getWind(...)` to particles every client tick.
+- `dev/protomanly/pmweather/particle/ParticleManager.java`
+ - Custom particle render manager.
+- `dev/protomanly/pmweather/particle/EntityRotFX.java`
+ - Base particle implementation with sorted translucent and block render types.
+- `dev/protomanly/pmweather/particle/ParticleCube.java`
+ - Renders spinning cube debris using block textures.
+- `dev/protomanly/pmweather/particle/ParticleTexFX.java`
+- `dev/protomanly/pmweather/particle/ParticleTexExtraRender.java`
+- `dev/protomanly/pmweather/particle/ParticleRegistry.java`
+- `dev/protomanly/pmweather/particle/behavior/ParticleBehavior.java`
+- `dev/protomanly/pmweather/entity/MovingBlock.java`
+- `dev/protomanly/pmweather/entity/client/MovingBlockRenderer.java`
+
+## Assets Included
+
+- `assets/pmweather/shaders/...`
+- `assets/pmweather/textures/particle/...`
+- `assets/minecraft/textures/effect/pmweather/...`
+- `assets/minecraft/atlases/particles.json`
+
+## What To Port First
+
+1. Port `Storm.getRankine(...)`, `Storm.getWind(...)`, and the two `pull(...)` methods.
+2. Port the `Vorticy` system if you want the small satellite swirls.
+3. Port the `WindEngine.getWind(...)` blending, because PMWeather uses that everywhere for visual motion.
+4. Port `ModShaders.java` plus `clouds.fsh` if you want the same volumetric funnel.
+5. Port `ParticleCube` plus the client particle tick path if you want the same debris behavior.
+
+## Expect Missing Dependencies
+
+These extracted files are the tornado stack, not a standalone compile-ready module.
+
+You will need to adapt references to PMWeather-specific infrastructure such as:
+
+- config classes
+- utility helpers
+- networking sync
+- sound registration
+- NeoForge event wiring
+- PMWeather random/logger helpers
+
+If you want, I can do a second pass and turn this into a cleaner drop-in package for your own mod namespace.
diff --git a/pmweather_tornado_extraction_export/assets/minecraft/atlases/blocks.json b/pmweather_tornado_extraction_export/assets/minecraft/atlases/blocks.json
new file mode 100644
index 00000000..f244cf32
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/minecraft/atlases/blocks.json
@@ -0,0 +1,8 @@
+{
+ "sources": [
+ {
+ "type": "minecraft:single",
+ "resource": "pmweather:block/anemometer"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/minecraft/atlases/particles.json b/pmweather_tornado_extraction_export/assets/minecraft/atlases/particles.json
new file mode 100644
index 00000000..61eaa406
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/minecraft/atlases/particles.json
@@ -0,0 +1,36 @@
+{
+ "sources": [
+ {
+ "type": "minecraft:single",
+ "resource": "pmweather:particle/rain"
+ },
+ {
+ "type": "minecraft:single",
+ "resource": "pmweather:particle/mist"
+ },
+ {
+ "type": "minecraft:single",
+ "resource": "pmweather:particle/splash"
+ },
+ {
+ "type": "minecraft:single",
+ "resource": "pmweather:particle/snow"
+ },
+ {
+ "type": "minecraft:single",
+ "resource": "pmweather:particle/snow1"
+ },
+ {
+ "type": "minecraft:single",
+ "resource": "pmweather:particle/snow2"
+ },
+ {
+ "type": "minecraft:single",
+ "resource": "pmweather:particle/snow3"
+ },
+ {
+ "type": "minecraft:single",
+ "resource": "pmweather:particle/sleet"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noise.png b/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noise.png
new file mode 100644
index 00000000..db96f6d0
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noise.png differ
diff --git a/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noisex.png b/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noisex.png
new file mode 100644
index 00000000..3d7b9530
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noisex.png differ
diff --git a/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noisey.png b/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noisey.png
new file mode 100644
index 00000000..b21d05e3
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noisey.png differ
diff --git a/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noisez.png b/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noisez.png
new file mode 100644
index 00000000..ad680492
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/minecraft/textures/effect/pmweather/noisez.png differ
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/include/noise.glsl b/pmweather_tornado_extraction_export/assets/pmweather/shaders/include/noise.glsl
new file mode 100644
index 00000000..bfcca816
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/include/noise.glsl
@@ -0,0 +1,95 @@
+#version 150
+
+vec3 mod289(vec3 x)
+{
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec4 mod289(vec4 x)
+{
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec4 permute(vec4 x)
+{
+ return mod289(((x*34.0)+1.0)*x);
+}
+
+vec4 taylorInvSqrt(vec4 r)
+{
+ return 1.79284291400159 - 0.85373472095314 * r;
+}
+
+vec3 fade(vec3 t) {
+ return t*t*t*(t*(t*6.0-15.0)+10.0);
+}
+
+// Classic Perlin noise
+float cnoise(vec3 P)
+{
+ vec3 Pi0 = floor(P); // Integer part for indexing
+ vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
+ Pi0 = mod289(Pi0);
+ Pi1 = mod289(Pi1);
+ vec3 Pf0 = fract(P); // Fractional part for interpolation
+ vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
+ vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
+ vec4 iy = vec4(Pi0.yy, Pi1.yy);
+ vec4 iz0 = Pi0.zzzz;
+ vec4 iz1 = Pi1.zzzz;
+
+ vec4 ixy = permute(permute(ix) + iy);
+ vec4 ixy0 = permute(ixy + iz0);
+ vec4 ixy1 = permute(ixy + iz1);
+
+ vec4 gx0 = ixy0 * (1.0 / 7.0);
+ vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
+ gx0 = fract(gx0);
+ vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
+ vec4 sz0 = step(gz0, vec4(0.0));
+ gx0 -= sz0 * (step(0.0, gx0) - 0.5);
+ gy0 -= sz0 * (step(0.0, gy0) - 0.5);
+
+ vec4 gx1 = ixy1 * (1.0 / 7.0);
+ vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
+ gx1 = fract(gx1);
+ vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
+ vec4 sz1 = step(gz1, vec4(0.0));
+ gx1 -= sz1 * (step(0.0, gx1) - 0.5);
+ gy1 -= sz1 * (step(0.0, gy1) - 0.5);
+
+ vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
+ vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
+ vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
+ vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
+ vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
+ vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
+ vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
+ vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
+
+ vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
+ g000 *= norm0.x;
+ g010 *= norm0.y;
+ g100 *= norm0.z;
+ g110 *= norm0.w;
+ vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
+ g001 *= norm1.x;
+ g011 *= norm1.y;
+ g101 *= norm1.z;
+ g111 *= norm1.w;
+
+ float n000 = dot(g000, Pf0);
+ float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
+ float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
+ float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
+ float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
+ float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
+ float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
+ float n111 = dot(g111, Pf1);
+
+ vec3 fade_xyz = fade(Pf0);
+ vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
+ vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
+ float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
+ return 2.2 * n_xyz;
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/post/clouds.json b/pmweather_tornado_extraction_export/assets/pmweather/shaders/post/clouds.json
new file mode 100644
index 00000000..00d4b672
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/post/clouds.json
@@ -0,0 +1,34 @@
+{
+ "targets": [
+ "clouds",
+ "final"
+ ],
+ "passes": [
+ {
+ "name": "pmweather:clouds",
+ "intarget": "minecraft:main",
+ "outtarget": "clouds",
+ "auxtargets":[
+ {"name": "DepthSampler", "id": "minecraft:main:depth"},
+ {"name": "NoiseSampler", "id": "pmweather/noise", "width": 512, "height": 512, "bilinear": true},
+ {"name": "NoiseSamplerX", "id": "pmweather/noisex", "width": 512, "height": 512, "bilinear": true},
+ {"name": "NoiseSamplerY", "id": "pmweather/noisey", "width": 512, "height": 512, "bilinear": true},
+ {"name": "NoiseSamplerZ", "id": "pmweather/noisez", "width": 512, "height": 512, "bilinear": true}
+ ]
+ },
+ {
+ "name": "pmweather:blur",
+ "intarget": "minecraft:main",
+ "outtarget": "final",
+ "auxtargets":[
+ {"name": "DepthSampler", "id": "minecraft:main:depth"},
+ {"name": "CloudSampler", "id": "clouds"}
+ ]
+ },
+ {
+ "name": "blit",
+ "intarget": "final",
+ "outtarget": "minecraft:main"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/blur.fsh b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/blur.fsh
new file mode 100644
index 00000000..1292dd7a
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/blur.fsh
@@ -0,0 +1,115 @@
+#version 330
+
+uniform sampler2D DiffuseSampler;
+uniform sampler2D DownsampledDepthSampler;
+uniform sampler2D DepthSampler;
+uniform sampler2D CloudSampler;
+uniform float downsample;
+uniform float glowFix;
+uniform float doBlur;
+
+uniform vec2 OutSize;
+
+in vec2 texCoord;
+
+out vec4 fragColor;
+
+#define near 0.05
+#define far 6000.0
+float linearizeDepth(float depth) {
+ float z = depth * 2.0 - 1.0;
+ return (2.0 * near * far) / (far + near - z * (far - near));
+}
+
+vec4 textureBilinear(sampler2D s, vec2 uv){
+ vec4 tl = texture(s, uv);
+ vec4 tr = textureOffset(s, uv, ivec2(1,0));
+ vec4 bl = textureOffset(s, uv, ivec2(0,1));
+ vec4 br = textureOffset(s, uv, ivec2(1,1));
+
+ vec2 f = fract(uv*OutSize);
+
+ vec4 ta = mix(tl, tr, f.x);
+ vec4 tb = mix(bl, br, f.x);
+
+ return mix(ta, tb, f.y);
+}
+
+vec4 textureBilinearOffset(sampler2D s, vec2 uv, ivec2 offset){
+ vec2 perc = vec2(1.0)/OutSize;
+ uv += perc*vec2(offset);
+ vec4 tl = texture(s, uv);
+ vec4 tr = textureOffset(s, uv, ivec2(1,0));
+ vec4 bl = textureOffset(s, uv, ivec2(0,1));
+ vec4 br = textureOffset(s, uv, ivec2(1,1));
+
+ vec2 f = fract(uv*OutSize);
+
+ vec4 ta = mix(tl, tr, f.x);
+ vec4 tb = mix(bl, br, f.x);
+
+ return mix(ta, tb, f.y);
+}
+
+void main(){
+ vec4 texel = texture(DiffuseSampler, texCoord);
+ vec4 cloud = textureBilinear(CloudSampler, texCoord/downsample);
+ float odepth = linearizeDepth(texture(DepthSampler, texCoord).r);
+
+ ivec2 off = ivec2(0,0);
+ if(downsample > 1.0 && glowFix > 0.5){
+ vec4 a = textureBilinearOffset(CloudSampler, texCoord/downsample, ivec2(1,0));
+ if(a.r > cloud.r){
+ off = ivec2(1,0);
+ }
+ cloud = max(cloud, a);
+
+ a = textureBilinearOffset(CloudSampler, texCoord/downsample, ivec2(-1,0));
+ if(a.r > cloud.r){
+ off = ivec2(-1,0);
+ }
+ cloud = max(cloud, a);
+
+ a = textureBilinearOffset(CloudSampler, texCoord/downsample, ivec2(0,1));
+ if(a.r > cloud.r){
+ off = ivec2(0,1);
+ }
+ cloud = max(cloud, a);
+
+ a = textureBilinearOffset(CloudSampler, texCoord/downsample, ivec2(0,-1));
+ if(a.r > cloud.r){
+ off = ivec2(0,-1);
+ }
+ cloud = max(cloud, a);
+ }
+
+ int res = 3;
+ float size = 0.0012;
+ int count = 1;
+
+ if(glowFix < 0.5 && doBlur > 0.5){
+ for(int x=-res; x <= res; x++){
+ for(int y=-res; y <= res; y++){
+ if(x != 0 || y != 0){
+ vec2 offset = (vec2(float(x), float(y))/float(res))*size;
+
+ float depth = linearizeDepth(texture(DepthSampler, texCoord + offset).r);
+
+ if(abs(depth-odepth) < 1){
+ cloud += textureBilinear(CloudSampler, (texCoord/downsample) + offset);
+ count++;
+ }
+ }
+ }
+ }
+ }
+
+ cloud /= count;
+
+ vec3 col = texel.rgb;
+ vec4 renderOut = cloud;
+
+ col = col * (1.0-renderOut.a) + renderOut.rgb;
+
+ fragColor = vec4(col, 1.0);
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/blur.json b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/blur.json
new file mode 100644
index 00000000..6eb05586
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/blur.json
@@ -0,0 +1,23 @@
+{
+ "blend": {
+ "func": "add",
+ "srcrgb": "srcalpha",
+ "dstrgb": "1-srcalpha"
+ },
+ "vertex": "blit",
+ "fragment": "pmweather:blur",
+ "attributes": ["Position"],
+ "samplers": [
+ {"name": "DiffuseSampler"},
+ {"name": "DepthSampler"},
+ {"name": "CloudSampler"}
+ ],
+ "uniforms": [
+ {"name": "downsample", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "glowFix", "type": "float", "count": 1, "values": [0]},
+ {"name": "doBlur", "type": "float", "count": 1, "values": [1]},
+
+ {"name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+ {"name": "OutSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ]}
+ ]
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/clouds.fsh b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/clouds.fsh
new file mode 100644
index 00000000..59865b80
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/clouds.fsh
@@ -0,0 +1,1168 @@
+#version 330
+
+#define PI 3.1415926535897932384626433832795
+
+struct CloudReturn{
+ float cloudDensity;
+ float rainDensity;
+ float dustDensity;
+ float brighten;
+};
+
+struct Render{
+ vec4 col;
+ float lightEnergy;
+ float lightningEnergy;
+ float depth;
+};
+
+float hash( float p ) {
+ p = fract(p * .1031);
+ p *= p + 33.33;
+ p *= p + p;
+ return fract(p);
+}
+
+float onoise(vec3 pos) {
+ // The noise function returns a value in the range -1.0f -> 1.0f
+
+ vec3 x = pos * 2.0;
+ vec3 p = floor(x);
+ vec3 f = fract(x);
+
+ f = f*f*(3.0-2.0*f);
+ float n = p.x + p.y*57.0 + 113.0*p.z;
+
+ return mix(mix(mix( hash(n+0.0), hash(n+1.0),f.x),
+ mix( hash(n+57.0), hash(n+58.0),f.x),f.y),
+ mix(mix( hash(n+113.0), hash(n+114.0),f.x),
+ mix( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
+}
+
+uniform sampler2D DiffuseSampler;
+uniform sampler2D DepthSampler;
+uniform sampler2D NoiseSampler;
+uniform sampler2D NoiseSamplerX;
+uniform sampler2D NoiseSamplerY;
+uniform sampler2D NoiseSamplerZ;
+
+uniform sampler2D dhDepthTex0;
+uniform sampler2D dhDepthTex1;
+uniform float hasDHDepth;
+uniform float dhNearPlane;
+uniform float dhFarPlane;
+uniform float dhRenderDistance;
+uniform mat4 dhProjection;
+uniform mat4 dhProjectionInverse;
+
+float noise2d(vec2 x){
+ x /= 512.0;
+ return (texture(NoiseSamplerX, x, -1.0).r-0.5)*2.0;
+}
+
+float noise(vec3 x){
+ x /= vec3(100.0,180.0,100.0)*3.0;
+
+ x.y = fract(x.y)*512.0;
+ float iz = floor(x.y);
+ float fz = fract(x.y);
+ vec2 a_off = vec2(23.0, 29.0)*(iz)/512.0;
+ vec2 b_off = vec2(23.0, 29.0)*(iz+1.0)/512.0;
+ float a = texture(NoiseSampler, x.xz + a_off, -1.0).r;
+ float b = texture(NoiseSampler, x.xz + b_off, -1.0).r;
+ return (mix(a,b,fz)-0.5)*2.0;
+}
+
+in vec2 texCoord;
+
+uniform float layer0height;
+uniform float layerCheight;
+uniform float stormSize;
+uniform float rain;
+uniform float snow;
+uniform float rainStrength;
+uniform int stormCount;
+uniform float stormPositions[48];
+uniform float stormVelocities[32];
+uniform float stormStages[16];
+uniform float stormEnergies[16];
+uniform float stormTypes[16];
+uniform float stormOcclusions[16];
+uniform float tornadoWindspeeds[16];
+uniform float tornadoWidths[16];
+uniform float tornadoTouchdownSpeeds[16];
+uniform float visualOnlys[16];
+uniform float stormDyings[16];
+uniform float stormSpins[16];
+uniform float tornadoShapes[16];
+uniform int lightningCount;
+uniform float lightningStrikes[192];
+uniform float lightningBrightness[64];
+
+uniform vec2 OutSize;
+uniform int maxSteps;
+uniform float downsample;
+uniform float stepSize;
+uniform float time;
+uniform float worldTime;
+uniform float overcastPerc;
+
+out vec4 fragColor;
+
+uniform vec3 pos;
+uniform vec2 scroll;
+uniform vec3 sunDir;
+uniform vec3 lightingColor;
+uniform vec3 skyColor;
+uniform mat4 proj;
+uniform mat4 viewmat;
+uniform mat4 vmat;
+uniform float lightIntensity;
+uniform float simpleLighting;
+
+uniform int quality;
+
+uniform float fogStart;
+uniform float fogEnd;
+
+uniform float _FOV;
+
+const float inf = uintBitsToFloat(0x7F800000u);
+
+uniform float nearPlane;
+uniform float farPlane;
+uniform float renderDistance;
+
+float linearizeDepth(float depth) {
+ float z = depth * 2.0 - 1.0;
+ return (2.0 * nearPlane * farPlane) / (farPlane + nearPlane - z * (farPlane - nearPlane));
+}
+
+float fbm(vec3 x, int octaves, float lacunarity, float gain, float amplitude){
+ float y = 0.0;
+
+ octaves -= int(floor(pow(4.0-quality, 0.75)));
+
+ for(int i = 0; i < max(octaves, 1); i++){
+ y += amplitude * noise(x);
+ x *= lacunarity;
+ amplitude *= gain;
+ }
+ return y;
+}
+
+vec3 getRayDir(vec2 screenUV){
+ vec2 uv = (screenUV*2.0)-1.0;
+ vec4 n = vec4(uv, 0.0, 1.0);
+ vec4 f = vec4(uv, 1.0, 1.0);
+ n = proj * n;
+ f = proj * f;
+ n.xyz /= n.w;
+ f.xyz /= f.w;
+ return normalize((viewmat*f).xyz - (viewmat*n).xyz);
+}
+
+mat2 spin(float angle){
+ return mat2(cos(angle),-sin(angle),sin(angle),cos(angle));
+}
+
+float distanceSqr(vec2 a, vec2 b){
+ vec2 c = a-b;
+ return dot(c,c);
+}
+
+vec2 nearestPoint(vec2 v, vec2 w, vec2 p){
+ float l2 = distanceSqr(v, w);
+ float t = clamp(dot(p-v, w-v) / l2, 0, 1);
+ return v + t * (w - v);
+}
+
+float minimumDistance(vec2 v, vec2 w, vec2 p){
+ float l2 = distanceSqr(v, w);
+ if(l2 == 0.0) return distance(p, v);
+
+ vec2 proj = nearestPoint(v,w,p);
+ return distance(p, proj);
+}
+
+float atan2(float y, float x){
+ return x == 0.0 ? sign(y)*PI/2 : atan(y, x);
+}
+
+CloudReturn getClouds(vec3 position, int octaveReduction){
+ float totalCloud = 0.0;
+ float totalBGCloud = 0.0;
+ float totalRain = 0.0;
+ float totalDust = 0.0;
+ float upperLevel = 0.0;
+
+ float nearestStorm = inf;
+
+ vec2 s = vec2(worldTime);
+
+ vec3 noisePos = position + vec3(s.x, -worldTime/2.0, s.y);
+ vec3 cloudNoisePos = position + vec3(worldTime*0.5, 0, worldTime*0.5);
+
+ vec3 noisePos2 = position + vec3(s.x*4.0, -worldTime/2.0, s.y*4.0);
+ vec3 cloudNoisePos2 = position + vec3(worldTime*1.5, 0, worldTime*1.5);
+
+ vec3 noisePosIT = position + vec3(s.x, worldTime/2.0, s.y);
+
+ float noise1 = -10.0;//fbm(noisePos/90.0, 5-octaveReduction, 2.0, 0.5, 1.0);
+ float noise2 = -10.0;//fbm(noisePosIT/120.0, 2-octaveReduction, 2.0, 0.3, 1.0);
+ float noise3 = -10.0;//noise2d(noisePos.xz/25.0)+(noise1*0.4);
+
+ if(octaveReduction == 0 && quality == 4){
+ octaveReduction = -2;
+ }
+
+ float bdensityNoise = min(noise2d(cloudNoisePos.xz/400.0), 1.0);
+ float bcloudNoise = min(noise2d(noisePos.xz/30.0), 1.0);
+ float bbaseNoise = noise2d(noisePos.xz/90.0);
+ float bheightNoise = noise2d(noisePos.zx/90.0);
+
+ float bgc = 0.0;
+ float bv = clamp(bdensityNoise-(1.0-overcastPerc), 0.0, 1.0);
+ bgc += max(pow(bv, 0.25), 0.0);
+ float bgCloudBase = mix(mix(0.0, 150.0, clamp((bbaseNoise+1)*0.5, 0.0, 1.0)), 0.0, bv*bv*bv)+layer0height;
+ float bgCloudHeight = mix(300.0, 850.0, clamp((bheightNoise+1)*0.5, 0.0, 1.0));
+ bgc *= mix(clamp((bcloudNoise-0.1)+bv, 0, 1), 1, bv);
+ bgc = pow(bgc, 0.5)*0.5;
+ bgc *= mix(bgCloudHeight/850.0, 1.0, sqrt(bv));
+
+ if(position.y > layer0height && position.y < layer0height+1000 && overcastPerc > 0){
+ float bgClouds = 0;
+ float detailNoise = fbm(noisePos/90.0, 5-octaveReduction, 2.0, 0.5, 1.0);
+ float densityNoise = min(bdensityNoise+(detailNoise*0.1), 1.0);
+ float cloudNoise = min(bcloudNoise+(detailNoise*0.3), 1.0);
+ float baseNoise = bbaseNoise+(detailNoise*0.1);
+ float heightNoise = bheightNoise+(detailNoise*0.1);
+
+ float v = clamp(densityNoise-(1.0-overcastPerc), 0.0, 1.0);
+ bgClouds += max(pow(v, 0.25), 0.0);
+
+ float base = mix(mix(0.0, 150.0, clamp((baseNoise+1)*0.5, 0.0, 1.0)), 0.0, v*v*v)+layer0height;
+ float height = mix(300.0, 850.0, clamp((heightNoise+1)*0.5, 0.0, 1.0));
+
+ bgClouds *= mix(clamp((cloudNoise-0.1)+(v*v), 0, 1), 1, v*v*v);
+
+ bgClouds *= 1.0 + detailNoise*0.2;
+
+ v = clamp((position.y-base)/height, 0.0, 1.0);
+ bgClouds -= v*v;
+ bgClouds *= 1-clamp((base-position.y)/50.0, 0.0, 1.0);
+
+ bgClouds = pow(bgClouds, 0.1)*0.5;
+
+ totalBGCloud = max(totalBGCloud, bgClouds);
+ upperLevel = max(upperLevel, bgClouds);
+ }
+
+ if(position.y > layerCheight && position.y < layerCheight+2000 && overcastPerc > 0){
+ float bgClouds = 0.0;
+ float detailNoise = fbm(noisePos2/150.0, 3-octaveReduction, 2.0, 0.5, 1.0);
+ float densityNoise = min(noise2d(cloudNoisePos2.xz/800.0)+(detailNoise*0.1), 1.0);
+ float warpNoiseX = noise2d(noisePos2.zx/400.0);
+ float warpNoiseY = noise2d(noisePos2.xz/400.0);
+ float cloudNoise = min(noise2d((noisePos2.xz/200.0) + (vec2(warpNoiseX, warpNoiseY)*100.0))+(detailNoise*0.2), 1.0);
+ float baseNoise = noise2d(noisePos2.xz/400.0)+(detailNoise*0.1);
+ float heightNoise = noise2d(noisePos2.zx/150.0)+(detailNoise*0.1);
+
+ float bandNoise = noise2d(noisePos2.xz/800.0)+(detailNoise*0.1);
+ float bandNoise2 = noise2d(noisePos2.zx/400.0)+(detailNoise*0.1);
+ float bandNoiseX = noise2d(noisePos2.xz/150.0)+(detailNoise*0.1);
+ float bandNoiseZ = noise2d(noisePos2.zx/150.0)+(detailNoise*0.1);
+
+ float v = clamp(densityNoise-(1.0-overcastPerc), 0.0, 1.0);
+ bgClouds += max(pow(v, 0.25), 0.0);
+
+ float base = mix(mix(0, 1000.0, clamp((baseNoise+1)*0.5, 0.0, 1.0)), 0.0, v*v*v)+layerCheight;
+ float height = mix(200.0, 500.0, clamp((heightNoise+1)*0.5, 0.0, 1.0));
+
+ bgClouds *= mix(clamp(sqrt(cloudNoise+0.3)+(v*v), 0.0, 1.0), 1, v*v*v);
+
+ bgClouds *= 1.0 + detailNoise*0.2;
+
+ vec3 bandingPos = noisePos2 + vec3(bandNoiseX, 0, bandNoiseZ)*1200.0;
+ float banding = sin((bandingPos.x+bandingPos.z)/(600.0 * (1 + bandNoise2*0.3)));
+ banding = abs(banding * banding * banding * banding);
+
+ bgClouds = mix(bgClouds, bgClouds*banding, clamp((bandNoise+1)*0.5, 0.0, 1.0));
+
+ v = clamp((position.y-base)/height, 0.0, 1.0);
+ bgClouds -= v*v;
+ bgClouds *= 1-clamp((base-position.y)/50.0, 0.0, 1.0);
+
+ v = clamp(densityNoise, 0.0, 1.0);
+ bgClouds = pow(bgClouds, 0.25)*mix(0.35, 0.0, v*v*v*v*v);
+
+ totalBGCloud = max(totalBGCloud, bgClouds);
+ }
+
+ for(int i = 0; i < stormCount; i++){
+ float clouds = 0.0;
+ float rainam = 0.0;
+ vec3 pos = vec3(stormPositions[i*3], stormPositions[(i*3)+1], stormPositions[(i*3)+2]);
+ vec2 vel = vec2(stormVelocities[i*2], stormVelocities[(i*2)+1]);
+ float stage = stormStages[i];
+ float energy = stormEnergies[i];
+ float windspeed = tornadoWindspeeds[i];
+ float width = max(tornadoWidths[i], 15.0);
+ float touchdownSpeed = tornadoTouchdownSpeeds[i];
+ float tornadoShape = tornadoShapes[i];
+ float stormSpin = stormSpins[i];
+ float stormType = stormTypes[i];
+ float occlusion = stormOcclusions[i];
+ bool visualOnly = false;
+ bool dying = false;
+ bool tornadic = false;
+
+ if(visualOnlys[i] > 0.0){
+ visualOnly = true;
+ }
+
+ if(stormDyings[i] > 0.0){
+ dying = true;
+ }
+
+ float smoothStage = stage + (energy/100.0);
+
+ // Hurricane
+ if(abs(stormType-2) < 0.1 && windspeed > 0){
+ float hHeight = layer0height+1000.0;
+ float heightP = (position.y-layer0height)/(hHeight-layer0height);
+ float sze = width;
+ pos += vec3(pow(heightP, 2.0)*sze*0.1, 0, -pow(heightP, 2.0)*sze*0.05);
+ float dist = distance(position.xz, pos.xz);
+
+ sze *= 1.0+(pow(heightP, 2.0)*0.4*(1.0+(clamp(dist/sze, 0.0, 1.0)*1.0)));
+
+ float eyeSize = 0.1;
+ float eyeCutSize = 0.1 + (heightP*0.2);
+
+ if(dist > sze*1.2 || position.y > hHeight){
+ continue;
+ }
+
+ if(noise1 < -9.0 && (position.y <= hHeight)){
+ noise1 = fbm(noisePos/90.0, 4-octaveReduction, 2.0, 0.5, 1.0);
+ noise2 = fbm(noisePosIT/120.0, 2-octaveReduction, 2.0, 0.3, 1.0);
+ noise3 = noise2d(noisePos.xz/25.0)+(noise1*0.4);
+ }
+
+ if(noise1 > -9.0){
+ dist *= 1.0+(((noise3+1.0)/2.0)*0.2);
+ }
+
+ float intensity = pow(clamp(windspeed/65.0, 0.0, 1.0), 0.85);
+
+ vec2 relPos = pos.xz-position.xz;
+
+ float d = sze/(3.0+(windspeed/12.0));
+ float d2 = sze/(1.15+(windspeed/12.0));
+ float dE = (sze*0.3)/(1.75+(windspeed/12.0));
+
+ float fac = 1.0+(max((dist-(sze*(eyeSize*2.0)))/sze, 0.0)*2.0);
+ d *= fac;
+ d2 *= fac;
+
+ float angle = ((atan2(relPos.y, relPos.x)-(dist/d)));
+ float angle2 = ((atan2(relPos.y, relPos.x)-(dist/d2)));
+ float angleE = ((atan2(relPos.y, relPos.x)-(dist/dE)));
+
+ float weak = 0.0;
+ float strong = 0.0;
+ float intense = 0.0;
+
+ float staticBands = sin(angle-(PI/2.0));
+ staticBands *= pow(clamp(dist/(sze*0.25), 0.0, 1.0), 0.1);
+ staticBands *= 1.25*pow(intensity, 0.75);
+ if(staticBands < 0){
+ weak += abs(staticBands);
+ }else{
+ weak += abs(staticBands) * pow(1.0-clamp(dist/(sze*0.65), 0.0, 1.0), 0.5);
+ weak *= clamp((windspeed-70.0)/40.0, 0.0, 1.0);
+ }
+
+ float rotatingBands = sin((angle2+radians(worldTime/8.0))*6.0);
+ rotatingBands *= pow(clamp(dist/(sze*0.25), 0.0, 1.0), 0.1);
+ rotatingBands *= 1.25*pow(intensity, 0.75);
+ strong += mix((abs(rotatingBands)*0.3)+0.7, weak, 0.45);
+ intense += mix((abs(rotatingBands)*0.2)+0.8, weak, 0.3);
+ weak = ((abs(rotatingBands)*0.3)+0.6)*weak;
+
+ float localCloud = 0.0;
+
+ localCloud += mix(mix(weak, strong, clamp((windspeed-40.0)/90.0, 0.0, 1.0)), intense, clamp((windspeed-120.0)/60.0, 0.0, 1.0));
+
+ float eye = sin((angleE+radians(worldTime/4.0))*2.0);
+ eye = mix(eye, 1.0, 1.0-clamp(dist/(sze*eyeSize), 0, 1));
+ localCloud = max(pow(1.0-clamp(dist/(sze*eyeSize*2.0), 0.0, 1.0), 0.5)*(abs(eye*0.3)+0.7)*1.4*intensity, localCloud);
+
+ localCloud *= pow(1.0-clamp(dist/sze, 0.0, 1.0), 0.5);
+ localCloud *= mix(1.0, pow(clamp(dist/(sze*eyeCutSize), 0.0, 1.0), 2.0), 0.5+(clamp((windspeed-65.0)/60.0, 0.0, 1.0)*0.5));
+
+ float noiseMain = (1.0+noise1)/2.0;
+ localCloud *= mix(0.8 + noiseMain*0.4, 1.0, clamp((windspeed-75.0)/50.0, 0.0, 1.0));
+ localCloud *= 0.8 + noiseMain*0.4;
+
+ float eyeBG = mix(1.0, pow(clamp(dist/(sze*eyeCutSize), 0, 1), 2), clamp((windspeed-65.0)/60.0, 0.0, 1.0));
+ totalBGCloud *= eyeBG;
+ bgc *= eyeBG;
+
+ if(localCloud > 0.7){
+ float dif = (localCloud-0.7)/3.0;
+ localCloud -= dif;
+ }
+
+ if(heightP >= 0.0 && heightP <= 1.0){
+ clouds += localCloud-(0.4*(1.0-pow((1.4*heightP)-0.4, 2.0)));
+ clouds *= pow(1.0-heightP, 0.5);
+ }
+
+ if(heightP <= 1.0){
+ float rain = max(localCloud-0.15, 0.0)*2.0;
+ rain *= pow(1.0-clamp(dist/width, 0.0, 1.0), 0.35);
+ rain *= 0.6 + noise2*0.5;
+ rain *= pow(1.0-heightP, 0.5);
+ rainam += rain*rainStrength;
+ }
+
+ clouds = sqrt(clouds)*0.8;
+ }
+
+ // Squall
+ if(abs(stormType-1) < 0.1){
+ if(stage >= 2.99){
+ smoothStage = 3.0;
+ }
+
+ float subcellular = clamp(smoothStage, 0.0, 1.0);
+ float cellular = clamp(smoothStage-0.5, 0.0, 1.0);
+
+ float stormHeight = mix(400.0, 1600.0, cellular);
+ float baseHeight = layer0height;
+
+ float v = clamp((position.y-layer0height)/1600.0, 0, 1);
+ v *= v;
+ vec2 right = normalize(vel.yx*vec2(1,-1));
+ vec2 fwd = normalize(vel.xy);
+ vec3 right3 = vec3(right.x, 0, right.y);
+
+ float rawDist = distance(position.xz, pos.xz);
+
+ vec3 l = right3*-(stormSize*5);
+ vec3 r = right3*(stormSize*5);
+
+ vec3 offset = vec3(-fwd.x, 0.0, -fwd.y)*pow(clamp(rawDist/(stormSize*5.0), 0.0, 1.0), 2.0)*(stormSize*1.5);
+ l += offset;
+ r += offset;
+
+ l += vec3(2000.0*v, 0, -900.0*v);
+ r += vec3(2000.0*v, 0, -900.0*v);
+
+ l += pos;
+ r += pos;
+
+ float dist = minimumDistance(l.xz, r.xz, position.xz);
+
+ float sze = stormSize*10.0;
+
+ if(position.y > layer0height+1000.0){
+ sze *= 8.0;
+ }
+
+ if(dist > sze){
+ continue;
+ }
+
+ if(noise1 < -9.0 && (dist < stormSize*1.6 || smoothStage > 1) && (position.y < stormHeight*1.5)){
+ noise1 = fbm(noisePos/90.0, 4-octaveReduction, 2.0, 0.5, 1.0);
+ noise2 = fbm(noisePosIT/120.0, 2-octaveReduction, 2.0, 0.3, 1.0);
+ noise3 = noise2d(noisePos.xz/25.0)+(noise1*0.4);
+ }
+
+ if(noise1 > -9){
+ baseHeight += noise1*15.0;
+ }
+
+ float noiseMain = noise1;
+
+ v = clamp((position.y-layer0height)/1600.0, 0, 1);
+ noiseMain = mix(noiseMain, noise3+(noise1*0.25), cellular*v);
+
+ float size = stormSize*1.5;
+ stormHeight *= 1 + noiseMain*0.1;
+
+ offset = vec3(fwd.x, 0, fwd.y)*stormSize*0.5;
+ l += offset;
+ r += offset;
+ vec2 nearPoint = nearestPoint(l.xz, r.xz, position.xz);
+ vec2 facing = position.xz-nearPoint;
+ float behind = -dot(facing, fwd);
+ behind += noise3*stormSize*0.2;
+
+ if(behind > 0.0){
+ baseHeight *= 1.0 - (pow(clamp(1.0-((behind-20.0)/stormSize), 0, 1), 4.0)*0.45*clamp(smoothStage-1.0, 0, 1.0));
+
+ float heightFromBase = position.y-baseHeight;
+
+ if(position.y < layer0height){
+ clouds -= clamp((heightFromBase-30.0)/30.0, 0, 1)*2.0;
+ }
+
+ float baseChange = pow(clamp((behind-stormSize)/(stormSize*4.0), 0, 1), 0.4)*300.0;
+ baseHeight += baseChange;
+ stormHeight -= baseChange;
+ }
+
+ float heightFromBase = position.y-baseHeight;
+ float heightPerc = clamp(heightFromBase/stormHeight, 0.0, 1.0);
+ float currentHeight = heightPerc*stormHeight;
+ float heightFromTop = stormHeight-currentHeight;
+
+ v = clamp(currentHeight/max(stormHeight, 1600.0), 0, 1.0);
+ v *= v;
+ pos += vec3(2000.0*v, 0, -900.0*v);
+
+ size = mix(size, mix(mix(size, size/2.0, cellular), size, sqrt(1.0-clamp(currentHeight/300.0, 0.0, 1.0))), pow(heightPerc, 0.25));
+
+ v = 1-clamp(heightFromTop/1600.0, 0.0, 1.0);
+ size = mix(size, mix(size, size*8.0, cellular), v*v*v*v*v);
+ size = mix(size, size/4.0, clamp(1.0-smoothStage, 0.0, 1.0));
+
+ size = mix(size, size*4, clamp(behind/(stormSize/4), 0.0, 1.0)*clamp(smoothStage-1.0, 0.0, 1.0));
+
+ size *= 1.0 + noiseMain*0.4;
+
+ float distPerc = clamp(dist/size, 0.0, 1.0);
+
+ if(distPerc >= 0.999){
+ continue;
+ }
+
+ if(position.y < baseHeight){
+ totalBGCloud *= distPerc*distPerc*distPerc;
+ }
+
+ float distBased = 1.0-distPerc;
+ float cloudField = (noise3-clamp(1.0-(smoothStage-1.0), 0.0, 1.0))*clamp(heightFromTop/65.0, 0.0, 1.0)*pow(1-distPerc, 0.25);
+ cloudField *= clamp(rawDist/mix(stormSize, stormSize*5.0, clamp(smoothStage/1.25, 0.0, 1.0)), 0.0, 1.0);
+
+ clouds += mix(0.0, mix(cloudField, distBased, clamp(smoothStage/2.0, 0.0, 1.0)), step(baseHeight, position.y)*step(position.y, baseHeight+stormHeight));
+
+ clouds *= clamp(heightFromTop/mix(100.0, 300.0, clamp(smoothStage*0.8, 0.0, 1.0)), 0.0, 1.0);
+
+ size = stormSize*0.75*clamp(smoothStage, 0.0, 1.0);
+ size = mix(size, size*8.0, clamp(behind/(stormSize/4.0), 0.0, 1.0)*clamp(smoothStage-1.0, 0.0, 1.0));
+ size *= 1.0 + noise2*0.4;
+ distPerc = clamp(dist/size, 0.0, 1.0);
+
+ clouds = sqrt(clouds)*0.8;
+
+ float rain = step(position.y, baseHeight);
+ rain *= 1.0-pow(distPerc, 1.35);
+ rain *= 0.6 + noise2*0.5;
+ rain *= clamp(smoothStage-0.5, 0.0, 1.0);
+
+ if(behind > 0.0){
+ rain = pow(rain, 2.5);
+ }
+
+ behind -= stormSize/3;
+
+ if(behind < 0.0){
+ rain *= 1.0-clamp(abs(behind)/(stormSize/3), 0.0, 1.0);
+ }
+
+ rainam += rain*rainStrength;
+ }
+
+ // Supercell
+ if(abs(stormType) < 0.1){
+ if(stage >= 2.99){
+ tornadic = true;
+ smoothStage = 3.0;
+ }
+
+ float visualOnlyStep = step(float(visualOnly), 0.5);
+ float subceullular = clamp(smoothStage-0.5, 0.0, visualOnlyStep);
+ float supercellular = clamp(smoothStage-1.5, 0.0, visualOnlyStep);
+
+ float stormHeight = mix(400.0, 1600.0, subceullular);
+ float baseHeight = layer0height;
+
+ float v = clamp((position.y-layer0height)/1600.0, 0, 1);
+ v *= v;
+ float dist = distance(position.xz, pos.xz + vec2(2000.0*v, -900.0*v));
+ float coreDist = distance(position.xz, pos.xz + vec2(2000.0, -900.0));
+ float sze = stormSize*2.0;
+
+ float clearBG = mix(1.0, pow(clamp(dist/(sze*2.0), 0.0, 1.0), 2.0), clamp(smoothStage, 0.0, 1.0));
+ totalBGCloud *= clearBG;
+ bgc *= clearBG;
+
+ if(position.y > layer0height+1000.0){
+ sze *= 8.0;
+ }else if(coreDist <= sze*2.0){
+ sze *= 2.5;
+ }
+
+ if(min(coreDist, dist) > sze){
+ continue;
+ }
+
+ if(noise1 < -9.0 && (dist < mix(stormSize*1.6, stormSize*0.85, 1-visualOnlyStep) || smoothStage > 1.0) && (position.y < stormHeight*1.5)){
+ noise1 = fbm(noisePos/90.0, 4-octaveReduction, 2.0, 0.5, 1.0);
+ noise2 = fbm(noisePosIT/120.0, 2-octaveReduction, 2.0, 0.3, 1.0);
+ noise3 = noise2d(noisePos.xz/25.0)+(noise1*0.4);
+ }
+
+ if(noise1 > -9.0){
+ baseHeight += noise1*15.0;
+ }
+
+ vec3 localPos = position-pos;
+ mat2 speen = spin((-time/(20.0*50.0))+(dist/(stormSize*2.0)));
+ mat2 speen2 = spin((-time/(20.0*15.0))+(dist/(stormSize/2.0)));
+
+ vec3 spinNoisePos = vec3(speen*localPos.xz, position.y-time);
+ vec3 spinNoisePos2 = vec3(speen2*localPos.xz, position.y-time);
+
+ float spinNoise1 = 0.0;//fbm(spinNoisePos/120.0, 5-octaveReduction, 2.0, 0.5, 1.0);
+ float spinNoise2 = 0.0;//fbm(spinNoisePos2/80.0, 5-octaveReduction, 2.0, 0.5, 1.0);
+
+ float heightFromBase = position.y-baseHeight;
+
+ if(position.y < baseHeight && smoothStage > 1.75 && position.y > baseHeight-120.0 && dist < stormSize){
+ spinNoise2 = fbm(spinNoisePos2/80.0, 4-octaveReduction, 2.0, 0.5, 1.0);
+ }else if(position.y > baseHeight && heightFromBase < 200.0 && smoothStage > 1.75 && dist < stormSize*2.5){
+ spinNoise1 = fbm(spinNoisePos/120.0, 4-octaveReduction, 2.0, 0.5, 1.0);
+ }
+
+ float noiseMain = mix(noise1, mix(spinNoise1, spinNoise2, step(position.y, baseHeight)), clamp((smoothStage-1.75)*3.0, 0.0, 1.0)*clamp(1.0-(heightFromBase/200.0), 0.0, 1.0));
+
+ v = clamp((position.y-layer0height)/1600.0, 0, 1);
+ noiseMain = mix(noiseMain, noise3+(noise1*0.25), supercellular*v);
+
+ float size = stormSize*1.5;
+ stormHeight *= 1.0 + noiseMain*0.1;
+
+ float heightPerc = clamp(heightFromBase/stormHeight, 0.0, 1.0);
+ float currentHeight = heightPerc*stormHeight;
+ float heightFromTop = stormHeight-currentHeight;
+
+ v = clamp(currentHeight/max(stormHeight, 1600.0), 0.0, 1.0);
+ v *= v;
+ pos += vec3(2000.0*v, 0, -900.0*v);
+ dist = distance(position.xz, pos.xz);
+
+ //float rawDistPerc = max(dist/size, 0);
+
+ size = mix(size, mix(mix(size, size/2.0, supercellular), size, sqrt(1-clamp(currentHeight/300, 0.0, 1.0))), pow(heightPerc, 0.25));
+
+ v = 1-clamp(heightFromTop/1600.0, 0.0, 1.0);
+ size = mix(size, mix(size, size*8.0, supercellular), v*v*v*v*v);
+ size = mix(size, size/4.0, clamp(1.0-smoothStage, 0.0, 1.0)*visualOnlyStep);
+ size = mix(size, size/2.0, 1.0-visualOnlyStep);
+
+ size *= 1.0 + noiseMain*0.4;
+ float distPerc = clamp(dist/size, 0.0, 1.0);
+ float coreDistPerc = clamp(coreDist/(size*4.0), 0.0, 1.0);
+
+ if(min(distPerc, coreDistPerc) >= 0.999){
+ continue;
+ }
+
+ float distBased = 1.0-distPerc;
+ float cloudField = (noise3-clamp(1.0-smoothStage, 0.0, 1.0))*clamp(heightFromTop/65.0, 0.0, 1.0)*pow(1.0-distPerc, 0.25);
+
+ clouds += mix(0, mix(cloudField, distBased, clamp(smoothStage*1.2, 0.0, visualOnlyStep)), step(baseHeight, position.y)*step(position.y, baseHeight+stormHeight));
+
+ clouds *= clamp(heightFromTop/mix(100.0, 300.0, clamp(smoothStage*0.8, 0.0, 1.0)), 0.0, 1.0);
+
+ //clouds *= 1 + clamp(noiseMain*2*clamp(1-smoothStage, 1-visualOnlyStep, 1), -1, 0.4);
+ //clouds -= clamp(1-smoothStage, 0, 1)*2;
+
+ size *= 0.35;
+ distPerc = clamp(dist/size, 0.0, 1.0);
+
+ float wallcloudLower = 120.0*pow(1.0-distPerc, 0.25)*clamp((smoothStage-2.0)*3.0, 0.0, 1.0);
+ float wallcloud = mix(0.0, 1.0-distPerc, step(position.y, baseHeight)*step(baseHeight-wallcloudLower, position.y));
+ wallcloud *= clamp((smoothStage-2.0)*5.0, 0.0, 1.0);
+ clouds += wallcloud;
+
+ float fnlTop = max(baseHeight-105.0, pos.y+30.0);
+ float torPerc = clamp(windspeed/touchdownSpeed, 0.0, 1.0);
+ float tornadoHeight = mix(fnlTop, pos.y-50.0, torPerc);
+
+ if(tornadic && position.y < baseHeight-wallcloudLower && position.y > pos.y-50.0 && dist < max(width*4.5, stormSize/3.0)){
+ float tornado = 1.0;
+ float percFnlHeight = clamp((position.y-pos.y)/(fnlTop-pos.y), 0.0, 1.0);
+ float percCos = (-cos(percFnlHeight*3.141592)+1.0)*0.5;
+
+ float torShape = mix(tornadoShape, 20.0, pow(clamp(width/500.0, 0.0, 1.0), 1.75));
+
+ float wid = (width/2.5) + ((width/2.5)*percFnlHeight*torPerc) + ((stormSize/mix(torShape+2.0, torShape, torPerc)) * percFnlHeight*percFnlHeight*percFnlHeight*percFnlHeight);
+ wid = mix(wid, 0.0, (1.0-percFnlHeight)*(1.0-torPerc));
+ float th = 1-clamp((position.y-tornadoHeight)/30.0, 0.0, 1.0);
+ wid = mix(wid, 0.0, th*th*th);
+ float maxWid = (width/4.0) + ((width/4.0)*torPerc) + ((stormSize/8.0) * torPerc);
+ vec3 torPos = pos;
+
+ float ropeMod = mix(3.0, 1.0, clamp(width/30.0, 0.0, 1.0));
+ ropeMod = mix(ropeMod, 1.0, clamp((windspeed-65.0)/30.0, 0.0, 1.0));
+ ropeMod = mix(0.1, ropeMod, clamp((windspeed/touchdownSpeed)*1.35, 0.0, 1.0));
+
+ float nx = onoise(vec3(pos.xz/500.0, time/200.0))*40.0*ropeMod;
+ float nz = onoise(vec3(time/200.0, pos.zx/500.0))*40.0*ropeMod;
+ vec3 attachmentPoint = vec3(nx, 0.0, nz);
+
+ float xAdd = onoise(vec3(pos.xz/250.0, (time/200.0)+((position.y*ropeMod)/50.0)))*20.0*ropeMod;
+ float zAdd = onoise(vec3((time/200.0)+((position.y*ropeMod)/50.0), pos.zx/250.0))*20.0*ropeMod;
+
+ float a = pow(percFnlHeight, 0.75);
+ xAdd *= a;
+ zAdd *= a;
+
+ torPos += mix(vec3(0), vec3(attachmentPoint.x, 0, attachmentPoint.z), percCos);
+ torPos += vec3(xAdd, 0, zAdd);
+
+ float torDist = distance(torPos.xz, position.xz);
+ vec3 localTorPos = position-torPos;
+
+ float widPerc = 1-clamp(torDist/wid, 0.0, 1.0);
+ float widMaxPerc = clamp(wid/maxWid, 0.0, 1.0);
+ float rotation = -stormSpin*3;
+ float rotation2 = -stormSpin/1.5;
+
+ mat2 torSpin = spin(rotation+(torDist/50.0));
+ mat2 torSpin2 = spin(rotation2+(torDist/150.0));
+ mat2 torSpin3 = spin(rotation2+(torDist/60.0));
+ vec3 torSpinPos = vec3(torSpin*localTorPos.xz, position.y-(time/2.0));
+ vec3 torSpinPos2 = vec3(torSpin2*localTorPos.xz, position.y-(time/2.0));
+ vec3 torSpinPos3 = vec3(torSpin3*localTorPos.xz, position.y-(time/2.0));
+
+ float nComp1 = fbm(torSpinPos/20.0, 3-octaveReduction, 2.0, 0.5, 1.0);
+ float nComp2 = fbm(torSpinPos2/40.0, 3-octaveReduction, 2.0, 0.5, 1.0);
+
+ float torNoise1 = mix(nComp1, nComp2, sqrt(widMaxPerc));
+
+ wid *= mix(0.8 + (torNoise1*0.2), 0.9, clamp(width/1000, 0.0, 1.0)*0.9);
+
+ widPerc = 1-clamp(torDist/wid, 0.0, 1.0);
+
+ tornado *= widPerc;
+ tornado = pow(tornado, 1.5)*4.0;
+ tornado *= clamp((position.y-tornadoHeight)/20.0, 0, 1.0);
+ tornado *= 0.8 + (torNoise1*0.2);
+
+ float dust = 1.0;
+ float dcNoise1 = fbm(torSpinPos3/20.0, 3-octaveReduction, 2.0, 0.5, 1.0);
+
+ float dcPerc = clamp((windspeed-45.0)/30.0, 0.0, 1.0);
+ float h = 40.0 + (dcNoise1*15.0);
+ float dcTop = pos.y+(max(dcPerc, 0.35)*h);
+ float percDCHeight = clamp((position.y-(pos.y-10.0))/(dcTop-pos.y), 0.0, 1.0);
+
+ wid = ((width/1.5) + ((width/1.5)*percFnlHeight*torPerc) + 25.0) + (25.0*pow(percDCHeight, 1.5)*pow(dcPerc, 0.75));
+ wid *= mix(0.6 + (dcNoise1*0.5), 0.85, clamp(width/500, 0.0, 1.0)*0.9);
+ widPerc = 1-clamp(torDist/wid, 0.0, 1.0);
+ widPerc = pow(widPerc, 0.25);
+ v = clamp(torDist/(wid*0.9), 0.0, 1.0);
+ widPerc *= v*v*v;
+ dust *= widPerc;
+ dust = pow(dust, 1.5)*0.15;
+ dust *= clamp((dcTop-position.y)/20, 0.0, 1.0);
+ dust *= clamp((position.y-(pos.y-20))/20, 0.0, 1.0);
+ dust *= 0.8 + (dcNoise1*0.2);
+ dust *= dcPerc;
+ dust *= 1.0-clamp((width-50.0)/200.0, 0.0, 1.0);
+ tornado = max(tornado, dust);
+ totalDust = max(totalDust, dust);
+
+ clouds = max(clouds, tornado);
+ }
+
+ size = stormSize*0.75*clamp(smoothStage, 0.0, 1.0);
+ size *= 1.0 + noise2*0.4;
+ distPerc = clamp(dist/size, 0.0, 1.0);
+ coreDistPerc = clamp(coreDist/(size*4.0), 0.0, 1.0);
+
+ clouds = sqrt(clouds)*0.8;
+
+ float rain = step(position.y, baseHeight)*visualOnlyStep;
+ rain *= 1.0-pow(distPerc, 1.35);
+ rain *= 0.6 + noise2*0.5;
+ rain *= clamp(smoothStage, 0.0, 1.0);
+ rainam += rain*rainStrength*mix(1.0, (occlusion*0.5)+0.5, clamp(smoothStage-2.0, 0.0, 1.0));
+
+ rain = step(position.y, baseHeight+1300.0)*visualOnlyStep;
+ rain *= 1.0-pow(coreDistPerc, 1.35);
+ rain *= 0.6 + noise2*0.5;
+ rain *= clamp((smoothStage-2.0)*2.0, 0.0, 1.0);
+ rainam = max(rainam, rain*rainStrength);
+ }
+
+ totalCloud = max(totalCloud, clouds);
+ totalRain = max(totalRain, rainam);
+ }
+
+ if(position.y <= bgCloudBase && bgc > 0.15){
+ if(noise2 < -9.0){
+ noise2 = fbm(noisePosIT/120.0, 2-octaveReduction, 2.0, 0.3, 1.0);
+ }
+
+ float rain = (bgc-0.15)*1.65;
+ rain *= 0.6 + noise2*0.5;
+ totalRain = max(totalRain, rain*rainStrength);
+ }
+
+ return CloudReturn(max(max(totalCloud, totalBGCloud), 0.0), max(totalRain, 0.0), max(totalDust, 0.0), max(upperLevel, 0.0));
+}
+
+vec3 worldPos(vec2 uv, float depth, mat4 invProj){
+ vec4 ndc;
+ ndc.xy = (uv-0.5)*2.0;
+ ndc.z = (depth-0.5)*2.0;
+ ndc.w = 1.0;
+
+ vec4 clip = invProj*ndc;
+ vec4 view = viewmat*(clip/clip.w);
+ vec3 result = view.xyz;
+
+ return result;
+}
+
+float BeersLaw(float dist, float absorbtion){
+ return exp(-dist * absorbtion);
+}
+
+float HenyeyGreenstein(float g, float mu){
+ float denom = 1.0 + g * (g - 2.0 * mu);
+ denom = sqrt(denom * denom * denom);
+ float heyey = (1.0 - g * g) / (4.0 * 3.14 * denom);
+ return mix(heyey, 1.0, 0.1);
+}
+
+float lightmarch(vec3 ro, vec3 dir, int steps, float marchSize){
+ float totalDensity = 0.0;
+
+ float d0 = 0.0;
+ for(int i = 0; i < steps; i++){
+ vec3 p = ro+(dir*d0);
+
+ CloudReturn d = getClouds(p, 8);
+ totalDensity += d.cloudDensity*0.005*marchSize;
+ d0 += marchSize;
+ marchSize += marchSize/4.0;
+
+ if(BeersLaw(totalDensity, 0.9) < 0.05){
+ break;
+ }
+ }
+
+ return BeersLaw(totalDensity, 0.9);
+}
+
+Render render(vec2 uv, float maxDepth){
+ float light = clamp((sunDir.y+0.1)/0.3, 0, 1);
+ vec3 ro = pos;
+ vec3 rd = getRayDir(uv);
+ float offset = onoise(vec3(uv*800.0, 0))*1;
+ float density = 0;
+ float totalRain = 0;
+ float rainDampen = 0;
+
+ vec4 res = vec4(0.0);
+
+ float d0 = 1.0;//nearPlane;
+
+ float ms = float(maxSteps);
+
+ bool inCloud = false;
+ int cyclesNotInCloud = 0;
+ int count = 0;
+
+ float totalTransmittance = 1.0;
+ float lightEnergy = 0.0;
+ float lightningEnergy = 0.0;
+
+ float phase = HenyeyGreenstein(0.3, dot(rd, sunDir));
+ float moonLight = 0.0;
+ float moonPhase = 0.0;
+
+ float depth = maxDepth;
+
+ if(sunDir.y < 0.1){
+ moonPhase = HenyeyGreenstein(0.3, dot(rd, sunDir*-1.0));
+ moonLight = clamp((sunDir.y-0.1)/-0.1, 0.0, 1.0)*0.25;
+ moonPhase = clamp(moonPhase*moonPhase*moonPhase*4.0, 0.0, 1.0);
+ }
+
+ for(int i = 0; i < ms; i++){
+ vec3 p = ro+(rd*d0);
+
+ if(d0 >= maxDepth || p.y > max(layerCheight+1000.0, layer0height+2000.0) || p.y < -64.0){
+ break;
+ }
+
+ float v = (count+5.0)/60.0;
+
+ float stepMult = v*v*v;
+ float multiplier = 1.0;
+
+ stepMult /= 2.0;
+
+ if(p.y > layer0height+500.0){
+ //stepMult *= 4;
+ }
+
+ if(quality == 0){
+ stepMult *= 4.0;
+ multiplier = 3.0;
+ }else if(quality == 1){
+ stepMult *= 2.0;
+ multiplier = 1.5;
+ }else if(quality == 4){
+ stepMult /= 2.0;
+ multiplier = 0.75;
+ }
+
+ if(!inCloud){
+ stepMult *= 4.0;
+ }
+
+ float smult = 1.0;
+ float s = max(((8.0+offset)*smult*stepMult), 0.5);
+
+ float rDist = renderDistance*2.0;
+
+ if(d0 < rDist){
+ CloudReturn clouds = getClouds(p, 0);
+
+ clouds.cloudDensity *= multiplier;
+ clouds.rainDensity *= multiplier;
+ clouds.dustDensity *= multiplier;
+
+ float sf = (snow*1.5*clamp(1.0-(d0/256.0), 0.0, 1.0));
+ float d = clouds.cloudDensity+sf;
+ float r = clouds.rainDensity;
+
+ d *= clamp(d0/25.0, 0.0, 1.0);
+ r *= clamp(d0/35.0, 0.0, 1.0);
+
+ d *= 1-clamp((d0-(rDist-1000.0))/1000.0, 0.0, 1.0);
+ r *= 1-clamp((d0-(rDist-1000.0))/1000.0, 0.0, 1.0);
+
+ float rd = d+(r*0.25*(s/8.0));
+
+ count++;
+
+ if(rd > 0){
+ if(d0 < depth){
+ depth = d0;
+ }
+
+ cyclesNotInCloud = 0;
+
+ if(!inCloud && d > 0){
+ inCloud = true;
+ d0 -= s;
+ count--;
+ continue;
+ }
+
+ density += rd*step(0.1, rd);
+ totalRain += r;
+
+ if(density > 5.0){
+ rd = max(rd, 2.0);
+ if(r > d){
+ r = 1.0;
+ }
+ }
+
+ if(d > 0.0 && totalRain < 0.01){
+ rainDampen += sqrt(d)*1.25;
+ }
+
+ float transmittance = mix(1.0, max(light, moonLight)*0.3, clamp(rd*4, 0.0, 1.0));
+
+ if(light > 0 && d-sf > 0 && totalTransmittance > 0.015 && simpleLighting < 0.5){
+ int steps = 0;
+
+ switch(quality){
+ case 0:
+ steps = 2;
+ break;
+
+ case 1:
+ steps = 4;
+ break;
+
+ case 2:
+ steps = 6;
+ break;
+
+ case 3:
+ steps = 8;
+ break;
+
+ case 4:
+ steps = 10;
+ break;
+
+ default:
+ steps = 4;
+ break;
+ }
+
+ transmittance = lightmarch(p, sunDir, steps, (((10.0-float(steps))/10.0)*20.0)+4.0);
+ }
+
+ if(moonLight > 0 && light <= 0 && d-sf > 0 && totalTransmittance > 0.015 && simpleLighting < 0.5){
+ int steps = 0;
+
+ switch(quality){
+ case 0:
+ steps = 2;
+ break;
+
+ case 1:
+ steps = 4;
+ break;
+
+ case 2:
+ steps = 6;
+ break;
+
+ case 3:
+ steps = 8;
+ break;
+
+ case 4:
+ steps = 10;
+ break;
+
+ default:
+ steps = 4;
+ break;
+ }
+
+ transmittance = lightmarch(p, sunDir*-1, steps, (((10.0-float(steps))/10.0)*20.0)+4.0);
+ }
+
+ if(lightningCount > 0 && totalTransmittance > 0.015){
+ for(int j = 0; j < lightningCount; j++){
+ vec3 lPos = vec3(lightningStrikes[j*3], layer0height, lightningStrikes[(j*3)+2]);
+ float bright = lightningBrightness[j];
+ float dist = distance(lPos.xz, p.xz);
+
+ float l = pow(1-clamp(dist/750.0, 0.0, 1.0), 2.0)*bright;
+
+ l *= 1-clamp((p.y-lPos.y)/150.0, 0.0, 1.0);
+ l *= rd;
+
+ lightningEnergy += totalTransmittance * l;
+ }
+ }
+
+ float luminance = 1.25 * (pow(d, 0.65)*(clamp(s, 10.0, 35.0)/35.0)) * max(phase, moonPhase);
+
+ vec3 cloudColor = mix(vec3(mix(0.5, 0.2, clamp(rain, 0.0, 1.0))), vec3(0.22, 0.302, 0.278), clamp(r, 0.0, 1.0));
+ vec3 dustColor = vec3(0.2, 0.125, 0.071);
+ float dustP = clamp(pow(clouds.dustDensity, 0.3), 0.0, 1.0);
+ dustColor = mix(dustColor*2.5, dustColor*0.5, hash(dustP*100.015));
+
+ cloudColor = mix(cloudColor, dustColor, clamp(pow(clouds.dustDensity, 0.1), 0.0, 1.0));
+ cloudColor = mix(cloudColor, skyColor*0.5, 0.6);
+ if(clouds.dustDensity <= 0.0){
+ cloudColor = mix(lightingColor*max(light, moonLight), cloudColor, clamp(pow(d, 0.65)+(r*5.0)+snow, 0, 1));
+ }
+ vec4 col = vec4(mix(cloudColor, vec3(0.0), clamp(rd, 0.0, 1.0))*light, clamp(rd, 0.0, 1.0));
+
+ col.rgb *= col.a;
+
+ res += col * (1.0-res.a);
+
+ lightEnergy += totalTransmittance * luminance;
+ totalTransmittance *= transmittance;
+
+ if(totalTransmittance < 0.2){
+ totalTransmittance = 0.0;
+ }
+ }else{
+ if(inCloud){
+ cyclesNotInCloud++;
+ }
+
+ if(cyclesNotInCloud >= 10.0){
+ inCloud = false;
+ cyclesNotInCloud = 0;
+ }
+ }
+
+ if(density > 4.0 || rd >= 1.5){
+ break;
+ }
+ }
+
+ d0 += s;
+ }
+
+ totalRain -= rainDampen;
+
+ float rv = clamp(1.0-rain, 0.0, 1.0);
+ return Render(res, clamp(lightEnergy, mix(0.3, 0.1, clamp(max(rain*4, totalRain*0.15), 0, 1)), clamp(density, 0, 1))*max(light, moonLight)*clamp(density, 0, 1)*rv*rv, lightningEnergy, depth);
+}
+
+float getDistFromDepth(float depth, vec2 uv, mat4 invProj) {
+ if (depth >= 1.0) return 1e10; // for sky
+ vec3 viewPos = worldPos(uv, depth, invProj);
+ return length(viewPos);
+}
+
+void main(){
+ vec2 uv = texCoord*downsample;
+ if(uv.x > 1.0 || uv.y > 1.0){
+ discard;
+ }
+
+ float maxTotalRenderDist = hasDHDepth > 0.5 ? max(renderDistance, dhRenderDistance) : renderDistance;
+ float sceneDistance = maxTotalRenderDist;
+ float vanillaDepth = texture(DepthSampler, uv).r;
+
+ if(vanillaDepth < 1.0){
+ sceneDistance = getDistFromDepth(vanillaDepth, uv, proj);
+ }
+
+ if(hasDHDepth > 0.5){
+ float dhDepth = texture(dhDepthTex0, uv).r;
+ if(dhDepth < 1.0){
+ float dhDistance = getDistFromDepth(dhDepth, uv, inverse(dhProjection));
+ sceneDistance = min(sceneDistance, dhDistance);
+ }
+
+ if(vanillaDepth >= 1.0 && dhDepth >= 1.0){
+ sceneDistance = maxTotalRenderDist*4.0;
+ }
+ }else{
+ if(vanillaDepth >= 1.0){
+ sceneDistance = maxTotalRenderDist*4.0;
+ }
+ }
+
+ /*vec3 wPos = worldPos(uv, rawDepth);
+ float depthCirc = min(length(wPos), farPlane);
+
+ if(rawDepth >= 1.0){
+ depthCirc = renderDistance*4.0;
+ }*/
+
+ sceneDistance = min(sceneDistance, maxTotalRenderDist*4.0);
+
+ Render renderOut = render(uv, sceneDistance);
+
+ fragColor = mix(mix(renderOut.col, vec4(lightingColor, renderOut.col.a), renderOut.lightEnergy*renderOut.col.a), vec4(vec3(1.0), renderOut.col.a), renderOut.lightningEnergy);
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/clouds.json b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/clouds.json
new file mode 100644
index 00000000..148f5dca
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/clouds.json
@@ -0,0 +1,79 @@
+{
+ "blend": {
+ "func": "add",
+ "srcrgb": "srcalpha",
+ "dstrgb": "1-srcalpha"
+ },
+ "vertex": "blit",
+ "fragment": "pmweather:clouds",
+ "attributes": ["Position"],
+ "samplers": [
+ {"name": "DiffuseSampler"},
+ {"name": "DepthSampler"},
+ {"name": "DHDepthSampler"},
+ {"name": "NoiseSampler"},
+ {"name": "NoiseSamplerX"},
+ {"name": "NoiseSamplerY"},
+ {"name": "NoiseSamplerZ"}
+ ],
+ "uniforms": [
+ {"name": "dhDepthTex0", "type": "int", "count": 1, "values": [7]},
+ {"name": "dhDepthTex1", "type": "int", "count": 1, "values": [8]},
+ {"name": "hasDHDepth", "type": "float", "count": 1, "values": [0.0]},
+ {"name": "dhNearPlane", "type": "float", "count": 1, "values": [0.05]},
+ {"name": "dhFarPlane", "type": "float", "count": 1, "values": [1024.0]},
+ {"name": "dhRenderDistance", "type": "float", "count": 1, "values": [4096.0]},
+ {"name": "dhProjection", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+ {"name": "dhProjectionInverse", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+ {"name": "dhViewmat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+ {"name": "stormCount", "type": "int", "count": 1, "values": [0]},
+ {"name": "lightningCount", "type": "int", "count": 1, "values": [0]},
+ {"name": "quality", "type": "int", "count": 1, "values": [2]},
+ {"name": "maxSteps", "type": "int", "count": 1, "values": [100]},
+ {"name": "stepSize", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "rainStrength", "type": "float", "count": 1, "values": [1.0]},
+ {"name": "nearPlane", "type": "float", "count": 1, "values": [0.05]},
+ {"name": "farPlane", "type": "float", "count": 1, "values": [256]},
+ {"name": "renderDistance", "type": "float", "count": 1, "values": [8000]},
+ {"name": "time", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "worldTime", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "layer0height", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "layerCheight", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "rain", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "snow", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "overcastPerc", "type": "float", "count": 1, "values": [0]},
+ {"name": "stormSize", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "lightIntensity", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "fogStart", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "downsample", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "fogEnd", "type": "float", "count": 1, "values": [0.1]},
+ {"name": "simpleLighting", "type": "float", "count": 1, "values": [0]},
+ {"name": "pos", "type": "float", "count": 3, "values": [0.0, 0.0, 0.0]},
+ {"name": "sunDir", "type": "float", "count": 3, "values": [0.0, 0.0, 0.0]},
+ {"name": "lightingColor", "type": "float", "count": 3, "values": [1.0, 1.0, 1.0]},
+ {"name": "skyColor", "type": "float", "count": 3, "values": [1.0, 1.0, 1.0]},
+ {"name": "scroll", "type": "float", "count": 2, "values": [0.0, 0.0]},
+ {"name": "lightningStrikes", "type": "float", "count": 192, "values": []},
+ {"name": "lightningBrightness", "type": "float", "count": 64, "values": []},
+ {"name": "stormPositions", "type": "float", "count": 48, "values": []},
+ {"name": "stormVelocities", "type": "float", "count": 32, "values": []},
+ {"name": "stormStages", "type": "float", "count": 16, "values": []},
+ {"name": "visualOnlys", "type": "float", "count": 16, "values": []},
+ {"name": "tornadoShapes", "type": "float", "count": 16, "values": []},
+ {"name": "stormEnergies", "type": "float", "count": 16, "values": []},
+ {"name": "stormOcclusions", "type": "float", "count": 16, "values": []},
+ {"name": "stormTypes", "type": "float", "count": 16, "values": []},
+ {"name": "tornadoWindspeeds", "type": "float", "count": 16, "values": []},
+ {"name": "tornadoWidths", "type": "float", "count": 16, "values": []},
+ {"name": "tornadoTouchdownSpeeds", "type": "float", "count": 16, "values": []},
+ {"name": "stormSpins", "type": "float", "count": 16, "values": []},
+
+ {"name": "viewmat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+ {"name": "vmat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+ {"name": "proj", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+
+ {"name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+ {"name": "OutSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ]},
+ { "name": "_FOV", "type": "float", "count": 1, "values": [ 70.0 ] }
+ ]
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/downsample.fsh b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/downsample.fsh
new file mode 100644
index 00000000..29bc0750
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/downsample.fsh
@@ -0,0 +1,18 @@
+#version 330
+
+uniform sampler2D DiffuseSampler;
+uniform sampler2D MainSampler;
+uniform float downsample;
+
+in vec2 texCoord;
+out vec4 fragColor;
+
+void main(){
+ vec2 uv = texCoord*downsample;
+ if(uv.x > 1 || uv.y > 1){
+ discard;
+ }
+
+ vec4 texel = texture(MainSampler, uv);
+ fragColor = vec4(texel.rgb, 1.0);
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/downsample.json b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/downsample.json
new file mode 100644
index 00000000..01ebef06
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/downsample.json
@@ -0,0 +1,20 @@
+{
+ "blend": {
+ "func": "add",
+ "srcrgb": "srcalpha",
+ "dstrgb": "1-srcalpha"
+ },
+ "vertex": "blit",
+ "fragment": "pmweather:blur",
+ "attributes": ["Position"],
+ "samplers": [
+ {"name": "DiffuseSampler"},
+ {"name": "MainSampler"}
+ ],
+ "uniforms": [
+ {"name": "downsample", "type": "float", "count": 1, "values": [0.1]},
+
+ {"name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+ {"name": "OutSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ]}
+ ]
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/smoothing.fsh b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/smoothing.fsh
new file mode 100644
index 00000000..e4f04856
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/smoothing.fsh
@@ -0,0 +1,16 @@
+#version 330
+
+uniform sampler2D DiffuseSampler;
+uniform sampler2D PreviousSampler;
+
+uniform vec2 OutSize;
+
+in vec2 texCoord;
+out vec4 fragColor;
+
+void main() {
+ vec4 texel = texture(DiffuseSampler, texCoord);
+ vec4 lastTexel = texture(PreviousSampler, texCoord);
+
+ fragColor = mix(lastTexel, texel, 0.3);
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/smoothing.json b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/smoothing.json
new file mode 100644
index 00000000..bcb24a1a
--- /dev/null
+++ b/pmweather_tornado_extraction_export/assets/pmweather/shaders/program/smoothing.json
@@ -0,0 +1,18 @@
+{
+ "blend": {
+ "func": "add",
+ "srcrgb": "srcalpha",
+ "dstrgb": "1-srcalpha"
+ },
+ "vertex": "blit",
+ "fragment": "pmweather:smoothing",
+ "attributes": ["Position"],
+ "samplers": [
+ {"name": "DiffuseSampler"},
+ {"name": "PreviousSampler"}
+ ],
+ "uniforms": [
+ {"name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]},
+ {"name": "OutSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ]}
+ ]
+}
\ No newline at end of file
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/mist.png b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/mist.png
new file mode 100644
index 00000000..51b50233
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/mist.png differ
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/rain.png b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/rain.png
new file mode 100644
index 00000000..ac8b8730
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/rain.png differ
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/sleet.png b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/sleet.png
new file mode 100644
index 00000000..2b856e57
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/sleet.png differ
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow.png b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow.png
new file mode 100644
index 00000000..c2104c5f
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow.png differ
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow1.png b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow1.png
new file mode 100644
index 00000000..aa83c3c6
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow1.png differ
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow2.png b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow2.png
new file mode 100644
index 00000000..c7c1c85c
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow2.png differ
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow3.png b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow3.png
new file mode 100644
index 00000000..9fd8a0d3
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/snow3.png differ
diff --git a/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/splash.png b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/splash.png
new file mode 100644
index 00000000..dbbf1904
Binary files /dev/null and b/pmweather_tornado_extraction_export/assets/pmweather/textures/particle/splash.png differ
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/compat/DistantHorizons.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/compat/DistantHorizons.java
new file mode 100644
index 00000000..e8ad76ea
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/compat/DistantHorizons.java
@@ -0,0 +1,70 @@
+package dev.protomanly.pmweather.compat;
+
+import dev.protomanly.pmweather.PMWeather;
+import org.joml.Matrix4f;
+
+public class DistantHorizons {
+ private static boolean initialized = false;
+ private static boolean dhPresent = false;
+ private static DistantHorizonsHandler handler = null;
+ private static final int DEFAULT_DEPTH_TEXTURE_ID = -1;
+ private static final Matrix4f DEFAULT_MATRIX = new Matrix4f();
+ private static final float DEFAULT_NEAR_PLANE = 0.05F;
+ private static final float DEFAULT_FAR_PLANE = 1024.0F;
+ private static final int DEFAULT_RENDER_DISTANCE = 256;
+
+ public DistantHorizons() {
+ super();
+ }
+
+ public static void initialize() {
+ if (!initialized) {
+ initialized = true;
+
+ try {
+ Class.forName("com.seibel.distanthorizons.api.DhApi");
+ handler = new DistantHorizonsHandler();
+ handler.initialize();
+ dhPresent = true;
+ PMWeather.LOGGER.info("Distant Horizons compatibility initialized");
+ } catch (NoClassDefFoundError | ClassNotFoundException var1) {
+ PMWeather.LOGGER.info("Distant Horizons not found, skipping integration");
+ dhPresent = false;
+ handler = null;
+ } catch (Exception e) {
+ PMWeather.LOGGER.error("Failed to initialize Distant Horizons compatibility", e);
+ dhPresent = false;
+ handler = null;
+ }
+
+ }
+ }
+
+ public static boolean isAvailable() {
+ return dhPresent && handler != null && handler.isReady();
+ }
+
+ public static int getDepthTextureId() {
+ return isAvailable() ? handler.getDepthTextureId() : -1;
+ }
+
+ public static Matrix4f getDhProjectionMatrix() {
+ return isAvailable() ? handler.getDhProjectionMatrix() : new Matrix4f(DEFAULT_MATRIX);
+ }
+
+ public static Matrix4f getDhModelViewMatrix() {
+ return isAvailable() ? handler.getDhModelViewMatrix() : new Matrix4f(DEFAULT_MATRIX);
+ }
+
+ public static float getNearPlane() {
+ return isAvailable() ? handler.getNearPlane() : 0.05F;
+ }
+
+ public static float getFarPlane() {
+ return isAvailable() ? handler.getFarPlane() : 1024.0F;
+ }
+
+ public static int getChunkRenderDistance() {
+ return isAvailable() ? handler.getChunkRenderDistance() : 256;
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/compat/DistantHorizonsHandler.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/compat/DistantHorizonsHandler.java
new file mode 100644
index 00000000..390e5cba
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/compat/DistantHorizonsHandler.java
@@ -0,0 +1,115 @@
+package dev.protomanly.pmweather.compat;
+
+import com.seibel.distanthorizons.api.DhApi;
+import com.seibel.distanthorizons.api.DhApi.Delayed;
+import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
+import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderPassEvent;
+import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderSetupEvent;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
+import com.seibel.distanthorizons.api.objects.DhApiResult;
+import dev.protomanly.pmweather.PMWeather;
+import org.joml.Matrix4f;
+
+public class DistantHorizonsHandler {
+ private boolean dhReady = false;
+ private int dhDepthTextureId = -1;
+ private Matrix4f dhProjectionMatrix = new Matrix4f();
+ private Matrix4f dhModelViewMatrix = new Matrix4f();
+ private float dhNearPlane = 0.05F;
+ private float dhFarPlane = 1024.0F;
+ private int dhRenderDistance = 256;
+
+ public DistantHorizonsHandler() {
+ super();
+ }
+
+ public void initialize() {
+ try {
+ this.registerEventHandlers();
+ } catch (Exception e) {
+ PMWeather.LOGGER.error("Failed to register DH event handlers", e);
+ throw e;
+ }
+ }
+
+ private void registerEventHandlers() {
+ DhApi.events.bind(DhApiAfterDhInitEvent.class, new DhApiAfterDhInitEvent() {
+ public void afterDistantHorizonsInit(DhApiEventParam event) {
+ PMWeather.LOGGER.info("Distant Horizons initialized");
+ DistantHorizonsHandler.this.dhReady = true;
+
+ try {
+ DistantHorizonsHandler.this.dhRenderDistance = (Integer)Delayed.configs.graphics().chunkRenderDistance().getValue();
+ } catch (Exception e) {
+ PMWeather.LOGGER.warn("Failed to get DH render distance, using default", e);
+ DistantHorizonsHandler.this.dhRenderDistance = 256;
+ }
+
+ }
+ });
+ DhApi.events.bind(DhApiBeforeRenderPassEvent.class, new DhApiBeforeRenderPassEvent() {
+ public void beforeRender(DhApiEventParam event) {
+ DistantHorizonsHandler.this.captureRenderState((DhApiRenderParam)event.value);
+ }
+ });
+ DhApi.events.bind(DhApiBeforeRenderSetupEvent.class, new DhApiBeforeRenderSetupEvent() {
+ public void beforeSetup(DhApiEventParam event) {
+ DistantHorizonsHandler.this.captureRenderState((DhApiRenderParam)event.value);
+ }
+ });
+ }
+
+ private void captureRenderState(DhApiRenderParam param) {
+ try {
+ DhApiResult depthResult = Delayed.renderProxy.getDhDepthTextureId();
+ if (depthResult.success && (Integer)depthResult.payload > 0) {
+ this.dhDepthTextureId = (Integer)depthResult.payload;
+ PMWeather.LOGGER.debug("Captured DH depth texture: " + this.dhDepthTextureId);
+ }
+
+ float[] projValues = param.dhProjectionMatrix.getValuesAsArray();
+ float[] mvValues = param.dhModelViewMatrix.getValuesAsArray();
+ this.dhProjectionMatrix.set(projValues[0], projValues[4], projValues[8], projValues[12], projValues[1], projValues[5], projValues[9], projValues[13], projValues[2], projValues[6], projValues[10], projValues[14], projValues[3], projValues[7], projValues[11], projValues[15]);
+ this.dhModelViewMatrix.set(mvValues[0], mvValues[4], mvValues[8], mvValues[12], mvValues[1], mvValues[5], mvValues[9], mvValues[13], mvValues[2], mvValues[6], mvValues[10], mvValues[14], mvValues[3], mvValues[7], mvValues[11], mvValues[15]);
+ this.dhNearPlane = param.nearClipPlane;
+ this.dhFarPlane = param.farClipPlane;
+
+ try {
+ this.dhRenderDistance = (Integer)Delayed.configs.graphics().chunkRenderDistance().getValue();
+ } catch (Exception var6) {
+ }
+ } catch (Exception e) {
+ PMWeather.LOGGER.error("Failed to capture DH render state", e);
+ }
+
+ }
+
+ public boolean isReady() {
+ return this.dhReady && this.dhDepthTextureId > 0;
+ }
+
+ public int getDepthTextureId() {
+ return this.dhDepthTextureId;
+ }
+
+ public Matrix4f getDhProjectionMatrix() {
+ return new Matrix4f(this.dhProjectionMatrix);
+ }
+
+ public Matrix4f getDhModelViewMatrix() {
+ return new Matrix4f(this.dhModelViewMatrix);
+ }
+
+ public float getNearPlane() {
+ return this.dhNearPlane;
+ }
+
+ public float getFarPlane() {
+ return this.dhFarPlane;
+ }
+
+ public int getChunkRenderDistance() {
+ return this.dhRenderDistance;
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/entity/MovingBlock.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/entity/MovingBlock.java
new file mode 100644
index 00000000..f6ae1cb8
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/entity/MovingBlock.java
@@ -0,0 +1,153 @@
+package dev.protomanly.pmweather.entity;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtUtils;
+import net.minecraft.network.syncher.EntityDataAccessor;
+import net.minecraft.network.syncher.EntityDataSerializers;
+import net.minecraft.network.syncher.SynchedEntityData;
+import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.MoverType;
+import net.minecraft.world.entity.Entity.MovementEmission;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.levelgen.Heightmap.Types;
+import net.minecraft.world.phys.Vec3;
+
+public class MovingBlock extends Entity {
+ public static EntityDataAccessor DATA_START_POS;
+ public static EntityDataAccessor DATA_BLOCK_STATE;
+
+ public MovingBlock(EntityType extends MovingBlock> entityType, Level level) {
+ super(entityType, level);
+ this.setBlockState(Blocks.STONE.defaultBlockState());
+ this.setStartPos(BlockPos.ZERO);
+ }
+
+ public MovingBlock(EntityType extends MovingBlock> entityType, Level level, BlockState blockstate, BlockPos startPos) {
+ super(entityType, level);
+ this.setBlockState(blockstate);
+ this.setStartPos(startPos);
+ }
+
+ public void tick() {
+ super.tick();
+ this.applyGravity();
+ this.move(MoverType.SELF, this.getDeltaMovement());
+ Vec3 motion = this.getDeltaMovement();
+ if (!this.level().isClientSide()) {
+ if (this.tickCount > 3600 || this.level().getNearestPlayer(this.getX(), this.getY(), this.getZ(), (double)96.0F, false) == null) {
+ this.discard();
+ return;
+ }
+
+ if (this.onGround() && this.tickCount > 40) {
+ if (this.level().getBlockState(this.blockPosition()).isAir()) {
+ this.level().setBlockAndUpdate(this.blockPosition(), this.getBlockState());
+ }
+
+ this.discard();
+ }
+ }
+
+ if (this.onGround()) {
+ BlockPos c = this.blockPosition();
+ BlockPos n = c.north(2);
+ BlockPos e = c.east(2);
+ BlockPos s = c.south(2);
+ BlockPos w = c.west(2);
+ n = this.level().getHeightmapPos(Types.MOTION_BLOCKING, n);
+ e = this.level().getHeightmapPos(Types.MOTION_BLOCKING, e);
+ s = this.level().getHeightmapPos(Types.MOTION_BLOCKING, s);
+ w = this.level().getHeightmapPos(Types.MOTION_BLOCKING, w);
+ if (n.getY() < c.getY()) {
+ c = n;
+ }
+
+ if (e.getY() < c.getY()) {
+ c = e;
+ }
+
+ if (s.getY() < c.getY()) {
+ c = s;
+ }
+
+ if (w.getY() < c.getY()) {
+ c = w;
+ }
+
+ Vec3 off = this.getPosition(1.0F).subtract(c.getCenter()).multiply((double)1.0F, (double)0.0F, (double)1.0F);
+ motion = motion.add(off.multiply((double)0.05F, (double)0.0F, (double)0.05F).multiply((double)1.0F, (double)0.0F, (double)1.0F));
+ }
+
+ this.setDeltaMovement(motion.multiply((double)0.99F, (double)0.99F, (double)0.99F));
+ }
+
+ public void setStartPos(BlockPos pos) {
+ this.entityData.set(DATA_START_POS, pos);
+ }
+
+ public BlockPos getStartPos() {
+ return (BlockPos)this.entityData.get(DATA_START_POS);
+ }
+
+ public void setBlockState(BlockState state) {
+ this.entityData.set(DATA_BLOCK_STATE, state);
+ }
+
+ public BlockState getBlockState() {
+ return (BlockState)this.entityData.get(DATA_BLOCK_STATE);
+ }
+
+ protected Entity.MovementEmission getMovementEmission() {
+ return MovementEmission.NONE;
+ }
+
+ public boolean canBeCollidedWith() {
+ return false;
+ }
+
+ public boolean shouldRenderAtSqrDistance(double distance) {
+ return distance < (double)327680.0F;
+ }
+
+ public boolean isAttackable() {
+ return false;
+ }
+
+ public boolean isPickable() {
+ return false;
+ }
+
+ protected double getDefaultGravity() {
+ return 0.04;
+ }
+
+ public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) {
+ return false;
+ }
+
+ protected void defineSynchedData(SynchedEntityData.Builder builder) {
+ builder.define(DATA_START_POS, BlockPos.ZERO);
+ builder.define(DATA_BLOCK_STATE, Blocks.STONE.defaultBlockState());
+ }
+
+ protected void readAdditionalSaveData(CompoundTag compoundTag) {
+ this.setBlockState(NbtUtils.readBlockState(this.level().holderLookup(Registries.BLOCK), compoundTag.getCompound("blockstate")));
+ this.setStartPos((BlockPos)NbtUtils.readBlockPos(compoundTag, "startPos").orElse(BlockPos.ZERO));
+ }
+
+ protected void addAdditionalSaveData(CompoundTag compoundTag) {
+ compoundTag.put("blockstate", NbtUtils.writeBlockState(this.getBlockState()));
+ compoundTag.put("startPos", NbtUtils.writeBlockPos(this.getStartPos()));
+ }
+
+ static {
+ DATA_START_POS = SynchedEntityData.defineId(MovingBlock.class, EntityDataSerializers.BLOCK_POS);
+ DATA_BLOCK_STATE = SynchedEntityData.defineId(MovingBlock.class, EntityDataSerializers.BLOCK_STATE);
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/entity/client/MovingBlockRenderer.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/entity/client/MovingBlockRenderer.java
new file mode 100644
index 00000000..5aec95c7
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/entity/client/MovingBlockRenderer.java
@@ -0,0 +1,61 @@
+package dev.protomanly.pmweather.entity.client;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.math.Axis;
+import dev.protomanly.pmweather.entity.MovingBlock;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.RenderType;
+import net.minecraft.client.renderer.block.BlockRenderDispatcher;
+import net.minecraft.client.renderer.entity.EntityRenderer;
+import net.minecraft.client.renderer.entity.EntityRendererProvider;
+import net.minecraft.client.renderer.texture.OverlayTexture;
+import net.minecraft.client.renderer.texture.TextureAtlas;
+import net.minecraft.client.resources.model.BakedModel;
+import net.minecraft.core.BlockPos;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.RandomSource;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.RenderShape;
+import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.client.RenderTypeHelper;
+import net.neoforged.neoforge.client.model.data.ModelData;
+import org.jetbrains.annotations.NotNull;
+
+public class MovingBlockRenderer extends EntityRenderer {
+ private final BlockRenderDispatcher dispatcher;
+
+ public MovingBlockRenderer(EntityRendererProvider.Context context) {
+ super(context);
+ this.shadowRadius = 0.5F;
+ this.dispatcher = context.getBlockRenderDispatcher();
+ }
+
+ public void render(MovingBlock entity, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
+ BlockState blockstate = entity.getBlockState();
+ float age = ((float)entity.tickCount + partialTicks) * 5.0F;
+ if (blockstate.getRenderShape() == RenderShape.MODEL) {
+ Level level = entity.level();
+ if (blockstate != level.getBlockState(entity.blockPosition()) && blockstate.getRenderShape() != RenderShape.INVISIBLE) {
+ poseStack.pushPose();
+ BlockPos pos = BlockPos.containing(entity.getX(), entity.getBoundingBox().maxY, entity.getZ());
+ poseStack.mulPose(Axis.XP.rotationDegrees(age * 2.0F));
+ poseStack.mulPose(Axis.YP.rotationDegrees(age * 2.0F));
+ poseStack.mulPose(Axis.ZP.rotationDegrees(age * 2.0F));
+ poseStack.translate((double)-0.5F, (double)0.0F, (double)-0.5F);
+ BakedModel model = this.dispatcher.getBlockModel(blockstate);
+
+ for(RenderType renderType : model.getRenderTypes(blockstate, RandomSource.create(blockstate.getSeed(entity.getStartPos())), ModelData.EMPTY)) {
+ this.dispatcher.getModelRenderer().tesselateBlock(level, model, blockstate, pos, poseStack, bufferSource.getBuffer(RenderTypeHelper.getMovingBlockRenderType(renderType)), false, RandomSource.create(), blockstate.getSeed(entity.getStartPos()), OverlayTexture.NO_OVERLAY, ModelData.EMPTY, renderType);
+ }
+
+ poseStack.popPose();
+ super.render(entity, entityYaw, partialTicks, poseStack, bufferSource, packedLight);
+ }
+ }
+
+ }
+
+ public @NotNull ResourceLocation getTextureLocation(@NotNull MovingBlock movingBlock) {
+ return TextureAtlas.LOCATION_BLOCKS;
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/event/GameBusClientEvents.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/event/GameBusClientEvents.java
new file mode 100644
index 00000000..0a8bc470
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/event/GameBusClientEvents.java
@@ -0,0 +1,522 @@
+package dev.protomanly.pmweather.event;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.config.ClientConfig;
+import dev.protomanly.pmweather.config.ServerConfig;
+import dev.protomanly.pmweather.interfaces.ParticleData;
+import dev.protomanly.pmweather.networking.ModNetworking;
+import dev.protomanly.pmweather.particle.EntityRotFX;
+import dev.protomanly.pmweather.particle.ParticleCube;
+import dev.protomanly.pmweather.particle.ParticleManager;
+import dev.protomanly.pmweather.particle.ParticleRegistry;
+import dev.protomanly.pmweather.particle.ParticleTexExtraRender;
+import dev.protomanly.pmweather.particle.ParticleTexFX;
+import dev.protomanly.pmweather.particle.behavior.ParticleBehavior;
+import dev.protomanly.pmweather.shaders.ModShaders;
+import dev.protomanly.pmweather.sound.ModSounds;
+import dev.protomanly.pmweather.util.ChunkCoordinatesBlock;
+import dev.protomanly.pmweather.weather.ThermodynamicEngine;
+import dev.protomanly.pmweather.weather.WeatherHandler;
+import dev.protomanly.pmweather.weather.WeatherHandlerClient;
+import dev.protomanly.pmweather.weather.WindEngine;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.particle.Particle;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.core.BlockPos;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.sounds.SoundEvent;
+import net.minecraft.sounds.SoundSource;
+import net.minecraft.util.Mth;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.levelgen.Heightmap.Types;
+import net.minecraft.world.level.material.MapColor;
+import net.minecraft.world.phys.Vec3;
+import net.minecraft.world.phys.shapes.VoxelShape;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.bus.api.SubscribeEvent;
+import net.neoforged.fml.common.EventBusSubscriber;
+import net.neoforged.fml.common.EventBusSubscriber.Bus;
+import net.neoforged.neoforge.client.event.ClientTickEvent;
+import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
+import net.neoforged.neoforge.client.event.ViewportEvent;
+import net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage;
+
+@EventBusSubscriber(
+ modid = "pmweather",
+ bus = Bus.GAME,
+ value = {Dist.CLIENT}
+)
+public class GameBusClientEvents {
+ public static Level lastLevel;
+ public static WeatherHandler weatherHandler;
+ public static ParticleManager particleManager;
+ public static ParticleManager particleManagerDebris;
+ public static ParticleBehavior particleBehavior = new ParticleBehavior((Vec3)null);
+ public static List LEAVES_BLOCKS = new ArrayList() {
+ {
+ this.add(Blocks.ACACIA_LEAVES);
+ this.add(Blocks.AZALEA_LEAVES);
+ this.add(Blocks.BIRCH_LEAVES);
+ this.add(Blocks.DARK_OAK_LEAVES);
+ this.add(Blocks.CHERRY_LEAVES);
+ this.add(Blocks.FLOWERING_AZALEA_LEAVES);
+ this.add(Blocks.MANGROVE_LEAVES);
+ this.add(Blocks.OAK_LEAVES);
+ this.add(Blocks.JUNGLE_LEAVES);
+ this.add(Blocks.SPRUCE_LEAVES);
+ }
+ };
+ public static ArrayList soundLocations = new ArrayList();
+ public static HashMap soundTimeLocations = new HashMap();
+ public static long lastAmbientTick;
+ public static long lastAmbientTickThreaded;
+ public static long lastWindSoundTick;
+
+ public GameBusClientEvents() {
+ super();
+ }
+
+ @SubscribeEvent
+ public static void fogEvent(ViewportEvent.RenderFog event) {
+ Minecraft minecraft = Minecraft.getInstance();
+ Level level = minecraft.level;
+ if (level != null && ClientConfig.baseGameFog) {
+ RenderSystem.setShaderFogStart(10000.0F);
+ RenderSystem.setShaderFogEnd(40000.0F);
+ }
+
+ }
+
+ @SubscribeEvent
+ public static void onStageRenderTick(RenderLevelStageEvent event) {
+ if (event.getStage() == Stage.AFTER_PARTICLES && weatherHandler != null) {
+ particleManagerDebris.render(event.getPoseStack(), (MultiBufferSource.BufferSource)null, Minecraft.getInstance().gameRenderer.lightTexture(), event.getCamera(), event.getPartialTick().getGameTimeDeltaPartialTick(false), event.getFrustum());
+ }
+
+ }
+
+ public static void doSnowParticles(float precip, Minecraft minecraft, Level level) {
+ int spawnsNeeded = (int)(precip * 80.0F);
+ int spawns = 0;
+ int spawnAreaSize = 50;
+
+ for(int i = 0; i < ClientConfig.rainParticleDensity; ++i) {
+ BlockPos pos = minecraft.player.blockPosition().offset(PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2, -5 + PMWeather.RANDOM.nextInt(25), PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2);
+ if (canPrecipitateAt(level, pos)) {
+ TextureAtlasSprite var10000;
+ switch (PMWeather.RANDOM.nextInt(4)) {
+ case 1 -> var10000 = ParticleRegistry.snow1;
+ case 2 -> var10000 = ParticleRegistry.snow2;
+ case 3 -> var10000 = ParticleRegistry.snow3;
+ default -> var10000 = ParticleRegistry.snow;
+ }
+
+ TextureAtlasSprite particle = var10000;
+ ParticleTexExtraRender snow = new ParticleTexExtraRender((ClientLevel)level, (double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)0.0F, (double)0.0F, (double)0.0F, particle);
+ snow.fullAlphaTarget = 1.0F;
+ snow.renderOrder = 3;
+ particleBehavior.initParticleSnow(snow, Math.max((int)(5.0F * precip), 1), (float)(WindEngine.getWind(pos, level, false, false, true).length() / (double)45.0F));
+ snow.setScale(Math.max(precip * 0.08F + (PMWeather.RANDOM.nextFloat() - PMWeather.RANDOM.nextFloat()) * 0.02F, 0.01F));
+ snow.windWeight = 0.15F;
+ snow.renderOrder = 3;
+ snow.spawnAsWeatherEffect();
+ ++spawns;
+ if (spawns > spawnsNeeded) {
+ break;
+ }
+ }
+ }
+
+ }
+
+ public static void doSleetParticles(float precip, Minecraft minecraft, Level level) {
+ int spawnsNeeded = (int)(precip * 300.0F);
+ int spawns = 0;
+ int spawnAreaSize = 30;
+
+ for(int i = 0; i < ClientConfig.rainParticleDensity; ++i) {
+ BlockPos pos = minecraft.player.blockPosition().offset(PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2, -5 + PMWeather.RANDOM.nextInt(25), PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2);
+ if (canPrecipitateAt(level, pos)) {
+ ParticleTexExtraRender sleet = new ParticleTexExtraRender((ClientLevel)level, (double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)0.0F, (double)0.0F, (double)0.0F, ParticleRegistry.sleet);
+ sleet.fullAlphaTarget = 1.0F;
+ sleet.renderOrder = 3;
+ particleBehavior.initParticleSleet(sleet, Math.max((int)(20.0F * precip), 1));
+ sleet.setScale(Math.max(precip * 0.08F + (PMWeather.RANDOM.nextFloat() - PMWeather.RANDOM.nextFloat()) * 0.02F, 0.02F) * 0.3F);
+ sleet.renderOrder = 3;
+ sleet.spawnAsWeatherEffect();
+ ++spawns;
+ if (spawns > spawnsNeeded) {
+ break;
+ }
+ }
+ }
+
+ }
+
+ public static void doRainParticles(float precip, Minecraft minecraft, Level level) {
+ int spawnsNeeded = (int)(precip * 300.0F);
+ int spawns = 0;
+ int spawnAreaSize = 30;
+ double windspeed = (double)0.0F;
+ if (weatherHandler != null) {
+ windspeed = WindEngine.getWind(minecraft.player.position(), level, false, false, false, true).length();
+ }
+
+ for(int i = 0; i < ClientConfig.rainParticleDensity; ++i) {
+ BlockPos pos = minecraft.player.blockPosition().offset(PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2, -5 + PMWeather.RANDOM.nextInt(25), PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2);
+ if (canPrecipitateAt(level, pos)) {
+ ParticleTexExtraRender rain = new ParticleTexExtraRender((ClientLevel)level, (double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)0.0F, (double)0.0F, (double)0.0F, ParticleRegistry.rain);
+ rain.fullAlphaTarget = Mth.lerp(precip, 0.3F, 1.0F);
+ rain.renderOrder = 3;
+ particleBehavior.initParticleRain(rain, Math.max((int)(20.0F * precip), 1));
+ if (windspeed > (double)50.0F && i < ClientConfig.rainParticleDensity / 3) {
+ float strength = precip * (float)Math.clamp((windspeed - (double)50.0F) / (double)50.0F, (double)0.0F, (double)1.0F);
+ ParticleTexExtraRender mist = new ParticleTexExtraRender((ClientLevel)level, (double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)0.0F, (double)0.0F, (double)0.0F, ParticleRegistry.mist);
+ mist.fullAlphaTarget = Mth.lerp(strength, 0.3F, 1.0F);
+ mist.renderOrder = 4;
+ particleBehavior.initParticleRain(mist, Math.max((int)(5.0F * strength), 1));
+ mist.setScale(0.5F + strength);
+ mist.setColor(0.9F, 0.9F, 0.9F);
+ mist.setGravity(0.5F);
+ }
+
+ ++spawns;
+ if (spawns > spawnsNeeded) {
+ break;
+ }
+ }
+ }
+
+ spawnAreaSize = 40;
+
+ for(int i = 0; (float)i < (float)(ClientConfig.rainParticleDensity * 3) * precip; ++i) {
+ BlockPos pos = minecraft.player.blockPosition().offset(PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2, -5 + PMWeather.RANDOM.nextInt(25), PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2);
+ pos = level.getHeightmapPos(Types.MOTION_BLOCKING, pos).below();
+ BlockState state = level.getBlockState(pos);
+ double maxY = (double)0.0F;
+ double minY = (double)0.0F;
+ VoxelShape shape = state.getShape(level, pos);
+ if (!shape.isEmpty()) {
+ minY = shape.bounds().minY;
+ maxY = shape.bounds().maxY;
+ }
+
+ if (!(pos.distSqr(minecraft.player.blockPosition()) > (double)spawnAreaSize / (double)2.0F * ((double)spawnAreaSize / (double)2.0F)) && canPrecipitateAt(level, pos.above())) {
+ if (level.getBlockState(pos).getBlock().defaultMapColor() == MapColor.WATER) {
+ pos = pos.offset(0, 1, 0);
+ }
+
+ ParticleTexFX rain = new ParticleTexFX((ClientLevel)level, (double)((float)pos.getX() + PMWeather.RANDOM.nextFloat()), (double)pos.getY() + 0.01 + maxY, (double)((float)pos.getZ() + PMWeather.RANDOM.nextFloat()), (double)0.0F, (double)0.0F, (double)0.0F, ParticleRegistry.splash);
+ rain.fullAlphaTarget = Mth.lerp(precip, 0.2F, 0.8F) / 2.0F;
+ rain.renderOrder = 5;
+ particleBehavior.initParticleGroundSplash(rain);
+ rain.spawnAsWeatherEffect();
+ }
+ }
+
+ }
+
+ @SubscribeEvent
+ public static void onTick(ClientTickEvent.Pre event) {
+ Minecraft minecraft = Minecraft.getInstance();
+ Level level = minecraft.level;
+ if (level != null && !minecraft.isPaused()) {
+ getClientWeather();
+ tryAmbientSounds();
+ trySounds();
+ weatherHandler.tick();
+ particleManager.tick();
+ particleManagerDebris.tick();
+ ModShaders.tick();
+ WeatherHandlerClient weatherHandlerClient = (WeatherHandlerClient)weatherHandler;
+ if (minecraft.player != null) {
+ Entity entity = minecraft.player;
+ Vec3 w = WindEngine.getWind(entity.getPosition(1.0F), level, false, true, false);
+ if (w.length() > (double)60.0F && !minecraft.player.isCreative() && !minecraft.player.isSpectator()) {
+ double factor = Mth.lerp(Math.clamp(w.length() / (double)125.0F, (double)0.0F, (double)1.0F), 0.005, 0.02);
+ float mult = 0.65F;
+ if (!entity.onGround()) {
+ mult = 0.15F;
+ }
+
+ entity.addDeltaMovement(w.multiply((double)0.05F, (double)0.0F, (double)0.05F).multiply(factor, (double)0.0F, factor).multiply((double)mult, (double)mult, (double)mult));
+ }
+
+ minecraft.particleEngine.iterateParticles((particle) -> {
+ if (particle instanceof ParticleData particleData) {
+ boolean affect = true;
+ if (particle instanceof EntityRotFX entityRotFX) {
+ affect = !entityRotFX.ignoreWind;
+ }
+
+ if (affect) {
+ Vec3 wind = WindEngine.getWind(particle.getPos(), level, false, false, false);
+ particleData.addVelocity(wind.multiply((double)0.05F, (double)0.05F, (double)0.05F).multiply((double)0.04F, (double)0.04F, (double)0.04F));
+ double l = wind.length() * 0.01;
+ if (particleData.getVelocity().length() < l) {
+ particleData.setVelocity(particleData.getVelocity().normalize().multiply(l, l, l));
+ }
+ }
+ }
+
+ });
+ particleManager.getParticles().forEach((particleRenderType, particles) -> {
+ for(Particle particle : particles) {
+ if (particle instanceof ParticleData particleData) {
+ float affect = 1.0F;
+ if (particle instanceof EntityRotFX entityRotFX) {
+ if (entityRotFX.ignoreWind) {
+ affect = 0.0F;
+ } else {
+ affect = entityRotFX.windWeight;
+ }
+ }
+
+ if (affect > 0.0F) {
+ Vec3 wind = WindEngine.getWind(particle.getPos(), level, false, false, false);
+ particleData.addVelocity(wind.multiply((double)0.05F, (double)0.05F, (double)0.05F).multiply((double)0.04F, (double)0.04F, (double)0.04F).multiply((double)affect, (double)affect, (double)affect));
+ double l = wind.length() * 0.01;
+ if (particleData.getVelocity().length() < l) {
+ particleData.setVelocity(particleData.getVelocity().normalize().multiply(l, l, l));
+ }
+ }
+ }
+ }
+
+ });
+ particleManagerDebris.getParticles().forEach((particleRenderType, particles) -> {
+ for(Particle particle : particles) {
+ if (particle instanceof ParticleData particleData) {
+ float affect = 1.0F;
+ if (particle instanceof EntityRotFX entityRotFX) {
+ if (entityRotFX.ignoreWind) {
+ affect = 0.0F;
+ } else {
+ affect = entityRotFX.windWeight;
+ }
+ }
+
+ if (affect > 0.0F) {
+ Vec3 wind = WindEngine.getWind(particle.getPos(), level, false, false, false);
+ particleData.addVelocity(wind.multiply((double)0.05F, (double)0.05F, (double)0.05F).multiply((double)0.04F, (double)0.04F, (double)0.04F).multiply((double)affect, (double)affect, (double)affect));
+ }
+ }
+ }
+
+ });
+ float hail = weatherHandlerClient.getHail();
+ float precip = weatherHandlerClient.getPrecipitation();
+ if (precip > 0.0F) {
+ ThermodynamicEngine.Precipitation precipType = ThermodynamicEngine.getPrecipitationType(weatherHandlerClient, minecraft.player.position(), level, 0);
+ if (precipType == ThermodynamicEngine.Precipitation.RAIN || precipType == ThermodynamicEngine.Precipitation.FREEZING_RAIN || precipType == ThermodynamicEngine.Precipitation.WINTRY_MIX) {
+ doRainParticles(precip, minecraft, level);
+ }
+
+ if (precipType == ThermodynamicEngine.Precipitation.SLEET || precipType == ThermodynamicEngine.Precipitation.WINTRY_MIX) {
+ doSleetParticles(precip, minecraft, level);
+ }
+
+ if (precipType == ThermodynamicEngine.Precipitation.SNOW || precipType == ThermodynamicEngine.Precipitation.WINTRY_MIX) {
+ doSnowParticles(precip, minecraft, level);
+ }
+ }
+
+ if (hail > 0.0F) {
+ int spawnsNeeded = (int)(hail * 80.0F);
+ int spawns = 0;
+ int spawnAreaSize = 30;
+
+ for(int i = 0; i < 15; ++i) {
+ BlockPos pos = minecraft.player.blockPosition().offset(PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2, -5 + PMWeather.RANDOM.nextInt(25), PMWeather.RANDOM.nextInt(spawnAreaSize) - spawnAreaSize / 2);
+ if (canPrecipitateAt(level, pos)) {
+ ParticleCube hailP = new ParticleCube((ClientLevel)level, (double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)0.0F, (double)0.0F, (double)0.0F, Blocks.PACKED_ICE.defaultBlockState());
+ particleBehavior.initParticleHail(hailP);
+ hailP.setScale(0.01F + PMWeather.RANDOM.nextFloat() * hail * 0.08F);
+ hailP.renderOrder = 3;
+ hailP.spawnAsDebrisEffect();
+ ++spawns;
+ if (spawns >= spawnsNeeded) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ public static boolean canPrecipitateAt(Level level, BlockPos pos) {
+ if ((double)pos.getY() > ServerConfig.layer0Height) {
+ return false;
+ } else {
+ return level.getHeightmapPos(Types.MOTION_BLOCKING, pos).getY() <= pos.getY();
+ }
+ }
+
+ public static void resetClientWeather() {
+ weatherHandler = null;
+ }
+
+ public static WeatherHandlerClient getClientWeather() {
+ try {
+ Level level = Minecraft.getInstance().level;
+ if (weatherHandler == null || level != lastLevel) {
+ init(level);
+ }
+ } catch (Exception e) {
+ PMWeather.LOGGER.error(e.getMessage(), e);
+ }
+
+ return (WeatherHandlerClient)weatherHandler;
+ }
+
+ public static void trySounds() {
+ try {
+ Minecraft minecraft = Minecraft.getInstance();
+ Level level = minecraft.level;
+ Player player = minecraft.player;
+ if (player == null || level == null) {
+ return;
+ }
+
+ float hail = ((WeatherHandlerClient)weatherHandler).getHail();
+ if (hail > 0.0F) {
+ int chance = (int)Mth.lerp(hail, 20.0F, 2.0F);
+ if (PMWeather.RANDOM.nextInt(chance) == 0) {
+ BlockPos pos = player.blockPosition().offset(PMWeather.RANDOM.nextInt(-15, 16), 15, PMWeather.RANDOM.nextInt(-15, 16));
+ pos = level.getHeightmapPos(Types.MOTION_BLOCKING, pos);
+ if (canPrecipitateAt(level, pos) && pos.distSqr(player.blockPosition()) < (double)225.0F) {
+ level.playLocalSound(pos, (SoundEvent)ModSounds.HAIL.value(), SoundSource.WEATHER, hail * 3.5F, 2.0F + PMWeather.RANDOM.nextFloat() * 0.5F, false);
+ }
+ }
+ }
+
+ if (lastWindSoundTick < System.currentTimeMillis()) {
+ lastWindSoundTick = System.currentTimeMillis() + 4000L + (long)PMWeather.RANDOM.nextInt(0, 3000);
+ Vec3 wind = WindEngine.getWind(player.position(), level);
+ double windspeed = wind.length();
+ if (windspeed > (double)55.0F) {
+ ModSounds.playPlayerLockedSound(player.position(), (SoundEvent)ModSounds.WIND_STRONG.value(), (float)(windspeed / (double)200.0F), 0.9F + PMWeather.RANDOM.nextFloat() * 0.2F);
+ }
+
+ if (windspeed > (double)35.0F) {
+ ModSounds.playPlayerLockedSound(player.position(), (SoundEvent)ModSounds.WIND_MED.value(), (float)(windspeed / (double)200.0F), 0.9F + PMWeather.RANDOM.nextFloat() * 0.2F);
+ }
+
+ if (windspeed > (double)5.0F) {
+ ModSounds.playPlayerLockedSound(player.position(), (SoundEvent)ModSounds.WIND_CALM.value(), Math.min((float)(windspeed / (double)100.0F), 0.1F), 0.9F + PMWeather.RANDOM.nextFloat() * 0.2F);
+ }
+ }
+
+ if (lastAmbientTick < System.currentTimeMillis()) {
+ lastAmbientTick = System.currentTimeMillis() + 500L;
+ int size = 32;
+ int hSize = size / 2;
+ BlockPos curBlockPos = player.blockPosition();
+
+ for(int i = 0; i < soundLocations.size(); ++i) {
+ ChunkCoordinatesBlock chunkCoord = soundLocations.get(i);
+ if (Math.sqrt(chunkCoord.distSqr(curBlockPos)) > (double)size) {
+ soundLocations.remove(i--);
+ soundTimeLocations.remove(chunkCoord);
+ } else {
+ Block block = level.getBlockState(chunkCoord).getBlock();
+ if (block != null && (block.defaultMapColor() == MapColor.WATER || block.defaultMapColor() == MapColor.PLANT)) {
+ long lastPlayTime = 0L;
+ float soundMuffle = 0.6F;
+ if (soundTimeLocations.containsKey(chunkCoord)) {
+ lastPlayTime = (Long)soundTimeLocations.get(chunkCoord);
+ }
+
+ float maxLeavesVolume = 1.0F;
+ soundMuffle *= (float)ClientConfig.leavesVolume;
+ if (lastPlayTime < System.currentTimeMillis() && LEAVES_BLOCKS.contains(chunkCoord.block)) {
+ Vec3 wind = WindEngine.getWind(curBlockPos, level, false, false, false);
+ double windspeed = wind.length();
+ soundTimeLocations.put(chunkCoord, System.currentTimeMillis() + 12000L + (long)PMWeather.RANDOM.nextInt(50));
+ minecraft.level.playLocalSound(chunkCoord, (SoundEvent)ModSounds.CALM_AMBIENCE.value(), SoundSource.AMBIENT, (float)Math.min((double)maxLeavesVolume, windspeed * (double)soundMuffle * (double)0.05F), 0.9F + PMWeather.RANDOM.nextFloat() * 0.2F, false);
+ }
+ } else {
+ soundLocations.remove(i);
+ soundTimeLocations.remove(chunkCoord);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ PMWeather.LOGGER.error(e.getMessage(), e);
+ }
+
+ }
+
+ public static void tryAmbientSounds() {
+ Minecraft minecraft = Minecraft.getInstance();
+ Level level = minecraft.level;
+ Player player = minecraft.player;
+ if (lastAmbientTickThreaded < System.currentTimeMillis() && ClientConfig.leavesVolume > (double)0.0F) {
+ lastAmbientTickThreaded = System.currentTimeMillis() + 500L;
+ int size = 32;
+ int hSize = size / 2;
+ BlockPos curBlockPos = player.blockPosition();
+
+ for(int x = curBlockPos.getX() - hSize; x < curBlockPos.getX() + hSize; ++x) {
+ for(int y = curBlockPos.getY() - hSize; y < curBlockPos.getY() + hSize; ++y) {
+ for(int z = curBlockPos.getZ() - hSize; z < curBlockPos.getZ() + hSize; ++z) {
+ Block block = level.getBlockState(new BlockPos(x, y, z)).getBlock();
+ if (block.defaultMapColor() == MapColor.PLANT) {
+ boolean proxFail = false;
+
+ for(ChunkCoordinatesBlock soundLocation : soundLocations) {
+ if (Math.sqrt(soundLocation.distSqr(new BlockPos(x, y, z))) < (double)15.0F) {
+ proxFail = true;
+ break;
+ }
+ }
+
+ if (!proxFail) {
+ soundLocations.add(new ChunkCoordinatesBlock(x, y, z, block));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ public static void init(Level level) {
+ lastLevel = level;
+ if (level != null) {
+ weatherHandler = new WeatherHandlerClient(level.dimension());
+ Minecraft minecraft = Minecraft.getInstance();
+ if (particleManager == null) {
+ particleManager = new ParticleManager(minecraft.level, minecraft.getTextureManager());
+ } else {
+ particleManager.setLevel((ClientLevel)level);
+ }
+
+ if (particleManagerDebris == null) {
+ particleManagerDebris = new ParticleManager(minecraft.level, minecraft.getTextureManager());
+ } else {
+ particleManagerDebris.setLevel((ClientLevel)level);
+ }
+
+ CompoundTag data = new CompoundTag();
+ data.putString("command", "syncFull");
+ data.putString("packetCommand", "WeatherData");
+ ModNetworking.clientSendToSever(data);
+ }
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/event/ModBusClientEvents.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/event/ModBusClientEvents.java
new file mode 100644
index 00000000..ea0e037c
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/event/ModBusClientEvents.java
@@ -0,0 +1,78 @@
+package dev.protomanly.pmweather.event;
+
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.block.entity.ModBlockEntities;
+import dev.protomanly.pmweather.entity.ModEntities;
+import dev.protomanly.pmweather.entity.client.MovingBlockRenderer;
+import dev.protomanly.pmweather.item.ModItems;
+import dev.protomanly.pmweather.item.component.ModComponents;
+import dev.protomanly.pmweather.render.AnemometerModel;
+import dev.protomanly.pmweather.render.AnemometerRenderer;
+import dev.protomanly.pmweather.render.RadarRenderer;
+import dev.protomanly.pmweather.render.SoundingViewerRenderer;
+import dev.protomanly.pmweather.render.WeatherBalloonModel;
+import dev.protomanly.pmweather.render.WeatherPlatformRenderer;
+import dev.protomanly.pmweather.shaders.ModShaders;
+import net.minecraft.client.renderer.entity.EntityRenderers;
+import net.minecraft.client.renderer.item.ItemProperties;
+import net.minecraft.client.renderer.item.ItemPropertyFunction;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.packs.resources.ResourceManager;
+import net.minecraft.server.packs.resources.SimplePreparableReloadListener;
+import net.minecraft.util.profiling.ProfilerFiller;
+import net.minecraft.world.item.Item;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.bus.api.SubscribeEvent;
+import net.neoforged.fml.common.EventBusSubscriber;
+import net.neoforged.fml.common.EventBusSubscriber.Bus;
+import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
+import net.neoforged.neoforge.client.event.EntityRenderersEvent;
+import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent;
+
+@EventBusSubscriber(
+ modid = "pmweather",
+ bus = Bus.MOD,
+ value = {Dist.CLIENT}
+)
+public class ModBusClientEvents {
+ public ModBusClientEvents() {
+ super();
+ }
+
+ @SubscribeEvent
+ public static void reloadListeners(RegisterClientReloadListenersEvent event) {
+ event.registerReloadListener(new SimplePreparableReloadListener() {
+ protected Object prepare(ResourceManager resourceManager, ProfilerFiller profilerFiller) {
+ return null;
+ }
+
+ protected void apply(Object o, ResourceManager resourceManager, ProfilerFiller profilerFiller) {
+ ModShaders.reload();
+ }
+ });
+ }
+
+ @SubscribeEvent
+ public static void onClientSetup(FMLClientSetupEvent event) {
+ EntityRenderers.register(ModEntities.MOVING_BLOCK.get(), MovingBlockRenderer::new);
+ registerItemProp(event, (Item)ModItems.CONNECTOR.get(), PMWeather.getPath("connected"), (itemStack, clientLevel, livingEntity, i) -> itemStack.has(ModComponents.WEATHER_BALLOON_PLATFORM) ? 1.0F : 0.0F);
+ }
+
+ public static void registerItemProp(FMLClientSetupEvent event, Item item, ResourceLocation propertyID, ItemPropertyFunction function) {
+ event.enqueueWork(() -> ItemProperties.register(item, propertyID, function));
+ }
+
+ @SubscribeEvent
+ public static void registerLayers(EntityRenderersEvent.RegisterLayerDefinitions event) {
+ event.registerLayerDefinition(AnemometerModel.LAYER_LOCATION, AnemometerModel::createBodyLayer);
+ event.registerLayerDefinition(WeatherBalloonModel.LAYER_LOCATION, WeatherBalloonModel::createBodyLayer);
+ }
+
+ @SubscribeEvent
+ public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) {
+ event.registerBlockEntityRenderer(ModBlockEntities.ANEMOMETER_BE.get(), AnemometerRenderer::new);
+ event.registerBlockEntityRenderer(ModBlockEntities.RADAR_BE.get(), RadarRenderer::new);
+ event.registerBlockEntityRenderer(ModBlockEntities.WEATHER_PLATFORM_BE.get(), WeatherPlatformRenderer::new);
+ event.registerBlockEntityRenderer(ModBlockEntities.SOUNDING_VIEWER_BE.get(), SoundingViewerRenderer::new);
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/interfaces/PostChainData.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/interfaces/PostChainData.java
new file mode 100644
index 00000000..8857ae1c
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/interfaces/PostChainData.java
@@ -0,0 +1,8 @@
+package dev.protomanly.pmweather.interfaces;
+
+import java.util.List;
+import net.minecraft.client.renderer.PostPass;
+
+public interface PostChainData {
+ List getPasses();
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/mixin/PostChainMixin.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/mixin/PostChainMixin.java
new file mode 100644
index 00000000..e43cfd25
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/mixin/PostChainMixin.java
@@ -0,0 +1,13 @@
+package dev.protomanly.pmweather.mixin;
+
+import java.util.List;
+import net.minecraft.client.renderer.PostChain;
+import net.minecraft.client.renderer.PostPass;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+@Mixin({PostChain.class})
+public interface PostChainMixin {
+ @Accessor("passes")
+ List getPasses();
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/EntityRotFX.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/EntityRotFX.java
new file mode 100644
index 00000000..92e04ccd
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/EntityRotFX.java
@@ -0,0 +1,459 @@
+package dev.protomanly.pmweather.particle;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferBuilder;
+import com.mojang.blaze3d.vertex.DefaultVertexFormat;
+import com.mojang.blaze3d.vertex.Tesselator;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+import com.mojang.blaze3d.vertex.VertexFormat.Mode;
+import com.mojang.math.Axis;
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.config.ClientConfig;
+import dev.protomanly.pmweather.event.GameBusClientEvents;
+import dev.protomanly.pmweather.particle.behavior.ParticleBehavior;
+import java.util.List;
+import net.minecraft.client.Camera;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.particle.ParticleRenderType;
+import net.minecraft.client.particle.TextureSheetParticle;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.client.renderer.texture.TextureAtlas;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.core.BlockPos;
+import net.minecraft.util.Mth;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.levelgen.Heightmap.Types;
+import net.minecraft.world.phys.AABB;
+import net.minecraft.world.phys.Vec3;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Quaternionf;
+import org.joml.Vector3f;
+
+public class EntityRotFX extends TextureSheetParticle {
+ public static final ParticleRenderType SORTED_TRANSLUCENT = new ParticleRenderType() {
+ public @NotNull BufferBuilder begin(Tesselator tesselator, @NotNull TextureManager textureManager) {
+ RenderSystem.disableCull();
+ RenderSystem.depthMask(false);
+ RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES);
+ RenderSystem.enableBlend();
+ RenderSystem.defaultBlendFunc();
+ return tesselator.begin(Mode.QUADS, DefaultVertexFormat.PARTICLE);
+ }
+
+ public String toString() {
+ return "PARTICLE_SHEET_SORTED_TRANSLUCENT";
+ }
+ };
+ public static final ParticleRenderType SORTED_OPAQUE_BLOCK = new ParticleRenderType() {
+ public @NotNull BufferBuilder begin(Tesselator tesselator, @NotNull TextureManager textureManager) {
+ RenderSystem.disableBlend();
+ RenderSystem.depthMask(true);
+ RenderSystem.setShader(GameRenderer::getParticleShader);
+ RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_BLOCKS);
+ return tesselator.begin(Mode.QUADS, DefaultVertexFormat.PARTICLE);
+ }
+
+ public String toString() {
+ return "PARTICLE_BLOCK_SHEET_SORTED_OPAQUE";
+ }
+ };
+ public float renderRange = 128.0F;
+ public int renderOrder = 0;
+ public float windWeight = 1.0F;
+ public int entityID = 0;
+ public float prevRotationYaw;
+ public float rotationYaw;
+ public float prevRotationPitch;
+ public float rotationPitch;
+ public float rotationRoll;
+ public boolean isTransparent = true;
+ public boolean weatherEffect = false;
+ public boolean killOnCollide = false;
+ public int killOnCollideActivateAtAge = 0;
+ public boolean dontRenderUnderTopmostBlock = false;
+ public boolean killWhenUnderTopmostBlock = false;
+ public int killWhenUnderTopmostBlock_ScanAheadRange = 0;
+ public int killWhenUnderCameraAtLeast = 0;
+ public int killWhenFarFromCameraAtLeast = 0;
+ public boolean facePlayer = false;
+ public boolean facePlayerYaw = false;
+ public boolean spinFast = false;
+ public float spinFastRate = 10.0F;
+ public boolean spinTowardsMotionDirection = false;
+ public float ticksFadeInMax = 0.0F;
+ public float ticksFadeOutMax = 0.0F;
+ public float ticksFadeOutMaxOnDeath = -1.0F;
+ public float ticksFadeOutCurOnDeath = 0.0F;
+ public boolean fadingOut = false;
+ public float fullAlphaTarget = 1.0F;
+ public float rotationAroundCenter = 0.0F;
+ public float rotationSpeedAroundCenter = 0.0F;
+ public boolean slantParticleToWind = false;
+ public boolean fastLight = false;
+ protected int lastNonZeroBrightness = 15728640;
+ public ParticleBehavior particleBehavior = null;
+ public boolean useCustomBBForRenderCulling = false;
+ public static final AABB INITIAL_AABB;
+ public AABB bbRender;
+ public boolean vanillaMotionDampen;
+ public boolean collisionSpeedDampen;
+ public boolean collidingHorizontally;
+ public boolean collidingDownwards;
+ public boolean collidingUpwards;
+ public boolean markCollided;
+ public boolean bounceOnVerticalImpact;
+ public float bounceOnVerticalImpactEnergy;
+ public boolean ignoreWind;
+ public ParticleRenderType renderType;
+
+ public EntityRotFX(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
+ super(level, x, y, z, xSpeed, ySpeed, zSpeed);
+ this.bbRender = INITIAL_AABB;
+ this.vanillaMotionDampen = true;
+ this.collisionSpeedDampen = true;
+ this.collidingHorizontally = false;
+ this.collidingDownwards = false;
+ this.collidingUpwards = false;
+ this.markCollided = false;
+ this.bounceOnVerticalImpact = false;
+ this.bounceOnVerticalImpactEnergy = 0.3F;
+ this.ignoreWind = false;
+ this.renderType = SORTED_TRANSLUCENT;
+ this.setSize(0.3F, 0.3F);
+ this.entityID = PMWeather.RANDOM.nextInt(100000);
+ }
+
+ public ParticleRenderType getRenderType() {
+ return this.renderType;
+ }
+
+ public void remove() {
+ if (this.particleBehavior != null) {
+ this.particleBehavior.particles.remove(this);
+ }
+
+ super.remove();
+ }
+
+ public void tick() {
+ super.tick();
+ this.prevRotationPitch = this.rotationPitch;
+ this.prevRotationYaw = this.rotationYaw;
+ Entity cam = Minecraft.getInstance().getCameraEntity();
+ if (!this.vanillaMotionDampen) {
+ this.xd /= (double)0.98F;
+ this.yd /= (double)0.98F;
+ this.zd /= (double)0.98F;
+ }
+
+ if (!this.removed && !this.fadingOut) {
+ if (this.killOnCollide && (this.killOnCollideActivateAtAge == 0 || this.age > this.killOnCollideActivateAtAge) && this.isColliding()) {
+ this.startDeath();
+ }
+
+ BlockPos pos = new BlockPos((int)this.x, (int)this.y, (int)this.z);
+ if (this.killWhenUnderTopmostBlock) {
+ int height = this.level.getHeightmapPos(Types.MOTION_BLOCKING, pos).getY();
+ if (this.y - (double)this.killWhenUnderTopmostBlock_ScanAheadRange <= (double)height) {
+ this.startDeath();
+ }
+ }
+
+ if (this.killWhenUnderCameraAtLeast != 0 && cam != null && this.y < cam.getY() - (double)this.killWhenUnderCameraAtLeast) {
+ this.startDeath();
+ }
+
+ if (this.killWhenFarFromCameraAtLeast != 0 && cam != null && this.age > 20 && this.age % 5 == 0 && cam.distanceToSqr(this.x, this.y, this.z) > (double)(this.killWhenFarFromCameraAtLeast * this.killWhenFarFromCameraAtLeast)) {
+ this.startDeath();
+ }
+ }
+
+ if (!this.collisionSpeedDampen && this.onGround) {
+ this.xd /= (double)0.7F;
+ this.zd /= (double)0.7F;
+ }
+
+ double speedXZ = Math.sqrt(this.getMotionX() * this.getMotionX() + this.getMotionZ() * this.getMotionZ());
+ double spinFastRateAdj = (double)this.spinFastRate * speedXZ * (double)10.0F;
+ if (this.spinFast) {
+ this.rotationPitch += (float)(this.entityID % 2 == 0 ? spinFastRateAdj : -spinFastRateAdj);
+ this.rotationYaw += (float)(this.entityID % 2 == 0 ? -spinFastRateAdj : spinFastRateAdj);
+ }
+
+ float angleToMovement = (float)Math.toDegrees(Math.atan2(this.xd, this.zd));
+ if (this.spinTowardsMotionDirection) {
+ this.rotationYaw = angleToMovement;
+ this.rotationPitch += this.spinFastRate;
+ }
+
+ if (!this.fadingOut) {
+ if (this.ticksFadeInMax > 0.0F && (float)this.age < this.ticksFadeInMax) {
+ this.setAlpha((float)this.age / this.ticksFadeInMax * this.fullAlphaTarget);
+ } else if (this.ticksFadeOutMax > 0.0F && (float)this.age > (float)this.lifetime - this.ticksFadeOutMax) {
+ float count = (float)this.getAge() - ((float)this.getLifetime() - this.ticksFadeOutMax);
+ float val = (this.ticksFadeOutMax - count) / this.ticksFadeOutMax;
+ this.setAlpha(val * this.fullAlphaTarget);
+ } else if (this.ticksFadeInMax > 0.0F || this.ticksFadeOutMax > 0.0F) {
+ this.setAlpha(this.fullAlphaTarget);
+ }
+ } else {
+ if (this.ticksFadeOutCurOnDeath < this.ticksFadeOutMaxOnDeath) {
+ ++this.ticksFadeOutCurOnDeath;
+ } else {
+ this.remove();
+ }
+
+ float val = 1.0F - this.ticksFadeOutCurOnDeath / this.ticksFadeOutMaxOnDeath;
+ this.setAlpha(val * this.fullAlphaTarget);
+ }
+
+ this.rotationAroundCenter += this.rotationSpeedAroundCenter;
+ this.rotationAroundCenter %= 360.0F;
+ this.tickExtraRotations();
+ }
+
+ public void tickExtraRotations() {
+ if (this.slantParticleToWind) {
+ double motionXZ = Math.sqrt(this.xd * this.xd + this.zd * this.zd);
+ this.rotationPitch = (float)Math.atan2(this.yd, motionXZ);
+ }
+
+ }
+
+ public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) {
+ Vec3 vec3d = renderInfo.getPosition();
+ float f = (float)(Mth.lerp((double)partialTicks, this.xo, this.x) - vec3d.x());
+ float f1 = (float)(Mth.lerp((double)partialTicks, this.yo, this.y) - vec3d.y());
+ float f2 = (float)(Mth.lerp((double)partialTicks, this.zo, this.z) - vec3d.z());
+ Quaternionf quaternion;
+ if (this.facePlayer || this.rotationPitch == 0.0F && this.rotationYaw == 0.0F) {
+ try {
+ quaternion = (Quaternionf)renderInfo.rotation().clone();
+ quaternion.mul(Axis.ZP.rotationDegrees(this.rotationRoll));
+ } catch (CloneNotSupportedException var16) {
+ quaternion = renderInfo.rotation();
+ }
+ } else {
+ quaternion = new Quaternionf(0.0F, 0.0F, 0.0F, 1.0F);
+ if (this.facePlayerYaw) {
+ quaternion.mul(Axis.YP.rotationDegrees(-renderInfo.getYRot()));
+ } else {
+ quaternion.mul(Axis.YP.rotationDegrees(Mth.lerp(partialTicks, this.prevRotationYaw, this.rotationYaw)));
+ }
+
+ quaternion.mul(Axis.XP.rotationDegrees(Mth.lerp(partialTicks, this.prevRotationPitch, this.rotationPitch)));
+ }
+
+ Vector3f[] v3f = new Vector3f[]{new Vector3f(-1.0F, -1.0F, 0.0F), new Vector3f(-1.0F, 1.0F, 0.0F), new Vector3f(1.0F, 1.0F, 0.0F), new Vector3f(1.0F, -1.0F, 0.0F)};
+ float scale = this.getQuadSize(partialTicks);
+
+ for(int i = 0; i < 4; ++i) {
+ Vector3f vector3f = v3f[i];
+ vector3f.rotate(quaternion);
+ vector3f.mul(scale);
+ vector3f.add(f, f1, f2);
+ }
+
+ float u0 = this.getU0();
+ float u1 = this.getU1();
+ float v0 = this.getV0();
+ float v1 = this.getV1();
+ int j = this.getLightColor(partialTicks);
+ if (j > 0) {
+ this.lastNonZeroBrightness = j;
+ } else {
+ j = this.lastNonZeroBrightness;
+ }
+
+ buffer.addVertex(v3f[0].x, v3f[0].y, v3f[0].z).setUv(u1, v1).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(j);
+ buffer.addVertex(v3f[1].x, v3f[1].y, v3f[1].z).setUv(u1, v0).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(j);
+ buffer.addVertex(v3f[2].x, v3f[2].y, v3f[2].z).setUv(u0, v0).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(j);
+ buffer.addVertex(v3f[3].x, v3f[3].y, v3f[3].z).setUv(u0, v1).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(j);
+ }
+
+ public void move(double x, double y, double z) {
+ double xx = x;
+ double yy = y;
+ double zz = z;
+ if (this.hasPhysics && (x != (double)0.0F || y != (double)0.0F || z != (double)0.0F)) {
+ Vec3 vec3d = Entity.collideBoundingBox((Entity)null, new Vec3(x, y, z), this.getBoundingBox(), this.level, List.of());
+ x = vec3d.x;
+ y = vec3d.y;
+ z = vec3d.z;
+ }
+
+ if (x != (double)0.0F || y != (double)0.0F || z != (double)0.0F) {
+ this.setBoundingBox(this.getBoundingBox().move(x, y, z));
+ if (this.useCustomBBForRenderCulling) {
+ this.bbRender = this.getBoundingBoxForRender().move(x, y, z);
+ }
+
+ this.setLocationFromBoundingbox();
+ }
+
+ this.onGround = yy != y && yy < (double)0.0F;
+ this.collidingHorizontally = xx != x || zz != z;
+ this.collidingDownwards = yy < y;
+ this.collidingUpwards = yy > y;
+ if (xx != x) {
+ this.xd = (double)0.0F;
+ }
+
+ if (zz != z) {
+ this.zd = (double)0.0F;
+ }
+
+ if (!this.markCollided) {
+ if (this.onGround || this.collidingDownwards || this.collidingHorizontally || this.collidingUpwards) {
+ this.onHit();
+ this.markCollided = true;
+ }
+
+ if (this.bounceOnVerticalImpact && (this.onGround || this.collidingDownwards)) {
+ this.setMotionY(-this.getMotionY() * (double)this.bounceOnVerticalImpactEnergy);
+ }
+ }
+
+ }
+
+ public void onHit() {
+ }
+
+ public void setSprite(@NotNull TextureAtlasSprite sprite) {
+ this.sprite = sprite;
+ }
+
+ public void spawnAsWeatherEffect() {
+ this.weatherEffect = true;
+ if (ClientConfig.customParticles) {
+ GameBusClientEvents.particleManager.add(this);
+ } else {
+ Minecraft.getInstance().particleEngine.add(this);
+ }
+
+ }
+
+ public void spawnAsDebrisEffect() {
+ this.weatherEffect = true;
+ if (ClientConfig.customParticles) {
+ GameBusClientEvents.particleManagerDebris.add(this);
+ } else {
+ Minecraft.getInstance().particleEngine.add(this);
+ }
+
+ }
+
+ public AABB getBoundingBoxForRender() {
+ return this.useCustomBBForRenderCulling ? this.bbRender : this.getBoundingBox();
+ }
+
+ public void setSizeForRenderCulling(float width, float height) {
+ if (width != this.bbWidth || height != this.bbHeight) {
+ this.bbWidth = width;
+ this.bbHeight = height;
+ AABB aabb = this.getBoundingBox();
+ double d0 = (aabb.minX + aabb.maxX - (double)width) / (double)2.0F;
+ double d1 = (aabb.minZ + aabb.maxZ - (double)width) / (double)2.0F;
+ this.bbRender = new AABB(d0, aabb.minY, d1, d0 + (double)this.bbWidth, aabb.minY + (double)this.bbHeight, d1 + (double)this.bbWidth);
+ }
+
+ }
+
+ public void startDeath() {
+ if (this.ticksFadeOutMaxOnDeath > 0.0F) {
+ this.ticksFadeOutCurOnDeath = 0.0F;
+ this.fadingOut = true;
+ } else {
+ this.remove();
+ }
+
+ }
+
+ public boolean isColliding() {
+ return this.onGround || this.collidingHorizontally;
+ }
+
+ public Vec3 getPivotedPosition() {
+ return Vec3.ZERO;
+ }
+
+ public double getX() {
+ return this.x;
+ }
+
+ public double getY() {
+ return this.y;
+ }
+
+ public double getZ() {
+ return this.z;
+ }
+
+ public int getAge() {
+ return this.age;
+ }
+
+ public void setMotionX(double val) {
+ this.xd = val;
+ }
+
+ public void setMotionY(double val) {
+ this.yd = val;
+ }
+
+ public void setMotionZ(double val) {
+ this.zd = val;
+ }
+
+ public double getMotionX() {
+ return this.xd;
+ }
+
+ public double getMotionY() {
+ return this.yd;
+ }
+
+ public double getMotionZ() {
+ return this.zd;
+ }
+
+ public void setGravity(float val) {
+ this.gravity = val;
+ }
+
+ public void setAlpha(float val) {
+ this.alpha = val;
+ }
+
+ public void setScale(float val) {
+ this.setSizeForRenderCulling(val, val);
+ this.quadSize = val;
+ }
+
+ public void setPrevPosX(double val) {
+ this.xo = val;
+ }
+
+ public void setPrevPosY(double val) {
+ this.yo = val;
+ }
+
+ public void setPrevPosZ(double val) {
+ this.zo = val;
+ }
+
+ public void setSize(float width, float height) {
+ super.setSize(width, height);
+ this.setPos(this.x, this.y, this.z);
+ }
+
+ public void setCanCollide(boolean val) {
+ this.hasPhysics = val;
+ }
+
+ static {
+ INITIAL_AABB = new AABB(Vec3.ZERO, Vec3.ZERO);
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleCube.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleCube.java
new file mode 100644
index 00000000..bbf9cc77
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleCube.java
@@ -0,0 +1,127 @@
+package dev.protomanly.pmweather.particle;
+
+import com.mojang.blaze3d.vertex.VertexConsumer;
+import com.mojang.math.Axis;
+import dev.protomanly.pmweather.PMWeather;
+import java.util.ArrayList;
+import java.util.List;
+import net.minecraft.client.Camera;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.particle.ParticleRenderType;
+import net.minecraft.client.renderer.block.BlockRenderDispatcher;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.resources.model.BakedModel;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.util.Mth;
+import net.minecraft.util.RandomSource;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Quaternionf;
+import org.joml.Vector3f;
+
+public class ParticleCube extends ParticleTexFX {
+ public ParticleCube(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, BlockState state) {
+ super(level, x, y, z, xSpeed, ySpeed, zSpeed, ParticleRegistry.rain);
+ TextureAtlasSprite sprite1 = this.getSpriteFromState(state);
+ if (sprite1 != null) {
+ this.setSprite(sprite1);
+ } else {
+ PMWeather.LOGGER.warn("Unable to find sprite from block {}", state);
+ sprite1 = this.getSpriteFromState(Blocks.OAK_PLANKS.defaultBlockState());
+ if (sprite1 != null) {
+ this.setSprite(sprite1);
+ }
+ }
+
+ int multiplier = Minecraft.getInstance().getBlockColors().getColor(state, this.level, new BlockPos((int)x, (int)y, (int)z), 0);
+ float mr = (float)(multiplier >>> 16 & 255) / 255.0F;
+ float mg = (float)(multiplier >>> 8 & 255) / 255.0F;
+ float mb = (float)(multiplier & 255) / 255.0F;
+ this.setColor(mr, mg, mb);
+ }
+
+ public TextureAtlasSprite getSpriteFromState(BlockState state) {
+ BlockRenderDispatcher blockRenderDispatcher = Minecraft.getInstance().getBlockRenderer();
+ BakedModel model = blockRenderDispatcher.getBlockModel(state);
+ Direction[] var4 = Direction.values();
+ int var5 = var4.length;
+ byte var6 = 0;
+ if (var6 < var5) {
+ Direction direction = var4[var6];
+ List list = model.getQuads(state, direction, RandomSource.create());
+ return !list.isEmpty() ? ((BakedQuad)list.getFirst()).getSprite() : model.getParticleIcon();
+ } else {
+ return null;
+ }
+ }
+
+ public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) {
+ Vec3 vec3d = renderInfo.getPosition();
+ float f = (float)(Mth.lerp((double)partialTicks, this.xo, this.x) - vec3d.x());
+ float f1 = (float)(Mth.lerp((double)partialTicks, this.yo, this.y) - vec3d.y());
+ float f2 = (float)(Mth.lerp((double)partialTicks, this.zo, this.z) - vec3d.z());
+ Quaternionf quaternion;
+ if (!this.facePlayer && (this.rotationPitch != 0.0F || this.rotationYaw != 0.0F)) {
+ quaternion = new Quaternionf(0.0F, 0.0F, 0.0F, 1.0F);
+ if (this.facePlayerYaw) {
+ quaternion.mul(Axis.YP.rotationDegrees(-renderInfo.getYRot()));
+ } else {
+ quaternion.mul(Axis.YP.rotationDegrees(Mth.lerp(this.rotationSpeedAroundCenter, this.prevRotationYaw, this.rotationYaw)));
+ }
+
+ quaternion.mul(Axis.XP.rotationDegrees(Mth.lerp(partialTicks, this.prevRotationPitch, this.rotationPitch)));
+ } else {
+ quaternion = renderInfo.rotation();
+ }
+
+ List faces = new ArrayList();
+ Vector3f[] face = new Vector3f[]{new Vector3f(-1.0F, -1.0F, -1.0F), new Vector3f(-1.0F, 1.0F, -1.0F), new Vector3f(1.0F, 1.0F, -1.0F), new Vector3f(1.0F, -1.0F, -1.0F)};
+ faces.add(face);
+ face = new Vector3f[]{new Vector3f(-1.0F, -1.0F, 1.0F), new Vector3f(-1.0F, 1.0F, 1.0F), new Vector3f(1.0F, 1.0F, 1.0F), new Vector3f(1.0F, -1.0F, 1.0F)};
+ faces.add(face);
+ face = new Vector3f[]{new Vector3f(-1.0F, -1.0F, -1.0F), new Vector3f(-1.0F, 1.0F, -1.0F), new Vector3f(-1.0F, 1.0F, 1.0F), new Vector3f(-1.0F, -1.0F, 1.0F)};
+ faces.add(face);
+ face = new Vector3f[]{new Vector3f(1.0F, -1.0F, -1.0F), new Vector3f(1.0F, 1.0F, -1.0F), new Vector3f(1.0F, 1.0F, 1.0F), new Vector3f(1.0F, -1.0F, 1.0F)};
+ faces.add(face);
+ face = new Vector3f[]{new Vector3f(-1.0F, -1.0F, -1.0F), new Vector3f(-1.0F, -1.0F, 1.0F), new Vector3f(1.0F, -1.0F, 1.0F), new Vector3f(1.0F, -1.0F, -1.0F)};
+ faces.add(face);
+ face = new Vector3f[]{new Vector3f(-1.0F, 1.0F, -1.0F), new Vector3f(-1.0F, 1.0F, 1.0F), new Vector3f(1.0F, 1.0F, 1.0F), new Vector3f(1.0F, 1.0F, -1.0F)};
+ faces.add(face);
+ float f4 = this.getQuadSize(partialTicks);
+
+ for(Vector3f[] entryFace : faces) {
+ for(int i = 0; i < 4; ++i) {
+ entryFace[i].rotate(quaternion);
+ entryFace[i].mul(f4);
+ entryFace[i].add(f, f1, f2);
+ }
+ }
+
+ float u0 = this.getU0();
+ float u1 = this.getU1();
+ float v0 = this.getV0();
+ float v1 = this.getV1();
+ int j = this.getLightColor(partialTicks);
+ if (j > 0) {
+ this.lastNonZeroBrightness = j;
+ } else {
+ j = this.lastNonZeroBrightness;
+ }
+
+ for(Vector3f[] entryFace : faces) {
+ buffer.addVertex(entryFace[0].x(), entryFace[0].y(), entryFace[0].z()).setUv(u1, v1).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(j);
+ buffer.addVertex(entryFace[1].x(), entryFace[1].y(), entryFace[1].z()).setUv(u1, v0).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(j);
+ buffer.addVertex(entryFace[2].x(), entryFace[2].y(), entryFace[2].z()).setUv(u0, v0).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(j);
+ buffer.addVertex(entryFace[3].x(), entryFace[3].y(), entryFace[3].z()).setUv(u0, v1).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(j);
+ }
+
+ }
+
+ public ParticleRenderType getRenderType() {
+ return SORTED_OPAQUE_BLOCK;
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleManager.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleManager.java
new file mode 100644
index 00000000..88dbc2a9
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleManager.java
@@ -0,0 +1,398 @@
+package dev.protomanly.pmweather.particle;
+
+import com.google.common.collect.EvictingQueue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferBuilder;
+import com.mojang.blaze3d.vertex.BufferUploader;
+import com.mojang.blaze3d.vertex.MeshData;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.Tesselator;
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.config.ClientConfig;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import net.minecraft.CrashReport;
+import net.minecraft.CrashReportCategory;
+import net.minecraft.ReportedException;
+import net.minecraft.Util;
+import net.minecraft.client.Camera;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.particle.Particle;
+import net.minecraft.client.particle.ParticleDescription;
+import net.minecraft.client.particle.ParticleProvider;
+import net.minecraft.client.particle.ParticleRenderType;
+import net.minecraft.client.particle.SpriteSet;
+import net.minecraft.client.particle.TrackingEmitter;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.client.renderer.LightTexture;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.culling.Frustum;
+import net.minecraft.client.renderer.texture.SpriteLoader;
+import net.minecraft.client.renderer.texture.TextureAtlas;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.core.particles.ParticleGroup;
+import net.minecraft.core.particles.ParticleOptions;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.resources.FileToIdConverter;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.packs.resources.PreparableReloadListener;
+import net.minecraft.server.packs.resources.Resource;
+import net.minecraft.server.packs.resources.ResourceManager;
+import net.minecraft.util.GsonHelper;
+import net.minecraft.util.RandomSource;
+import net.minecraft.util.profiling.ProfilerFiller;
+import net.neoforged.neoforge.client.ClientHooks;
+import org.apache.commons.compress.utils.Lists;
+import org.joml.Matrix4fStack;
+
+public class ParticleManager implements PreparableReloadListener {
+ private static final FileToIdConverter PARTICLE_LISTER = FileToIdConverter.json("particles");
+ private static final ResourceLocation PARTICLES_ATLAS_INFO = ResourceLocation.withDefaultNamespace("particles");
+ private static final List RENDER_ORDER;
+ protected ClientLevel level;
+ private final TextureAtlas textureAtlas;
+ private final TextureManager textureManager;
+ private final Map> providers = new HashMap>();
+ private final Map spriteSets = Maps.newHashMap();
+ private final Queue particlesToAdd = Queues.newArrayDeque();
+ private final Queue trackingEmitters = Queues.newArrayDeque();
+ private final Map> particles;
+ private final Object2IntOpenHashMap trackedParticleCounts;
+
+ public ParticleManager(ClientLevel level, TextureManager textureManager) {
+ super();
+ this.particles = Maps.newTreeMap(ClientHooks.makeParticleRenderTypeComparator(RENDER_ORDER));
+ this.trackedParticleCounts = new Object2IntOpenHashMap();
+ this.textureAtlas = new TextureAtlas(TextureAtlas.LOCATION_PARTICLES);
+ this.level = level;
+ this.textureManager = textureManager;
+ }
+
+ public CompletableFuture reload(PreparableReloadListener.PreparationBarrier preparationBarrier, ResourceManager resourceManager, ProfilerFiller profilerFiller, ProfilerFiller profilerFiller1, Executor executor, Executor executor1) {
+ CompletableFuture> completableFuture = CompletableFuture.supplyAsync(() -> PARTICLE_LISTER.listMatchingResources(resourceManager), executor).thenCompose((locationResourceMap) -> {
+ List> list = new ArrayList>(locationResourceMap.size());
+ locationResourceMap.forEach((k, v) -> {
+ ResourceLocation resourceLocation = PARTICLE_LISTER.fileToId(k);
+ list.add(CompletableFuture.supplyAsync(() -> {
+ record ParticleDefinition(ResourceLocation resourceLocation, Optional> sprites) {
+ ParticleDefinition {
+ super();
+ }
+ }
+
+ return new ParticleDefinition(resourceLocation, this.loadParticleDescription(resourceLocation, v));
+ }, executor));
+ });
+ return Util.sequence(list);
+ });
+ CompletableFuture completableFuture1 = SpriteLoader.create(this.textureAtlas).loadAndStitch(resourceManager, PARTICLES_ATLAS_INFO, 0, executor).thenCompose(SpriteLoader.Preparations::waitForUpload);
+ CompletableFuture var10000 = CompletableFuture.allOf(completableFuture1, completableFuture);
+ Objects.requireNonNull(preparationBarrier);
+ return var10000.thenCompose(preparationBarrier::wait).thenAcceptAsync((v) -> {
+ this.clearParticles();
+ profilerFiller1.startTick();
+ profilerFiller1.push("upload");
+ SpriteLoader.Preparations preparations = completableFuture1.join();
+ this.textureAtlas.upload(preparations);
+ profilerFiller1.popPush("bindSpriteSets");
+ Set set = new HashSet();
+ TextureAtlasSprite textureAtlasSprite = preparations.missing();
+ ((List)completableFuture.join()).forEach((particleDefinition) -> {
+ Optional> optionalResourceLocations = particleDefinition.sprites();
+ if (!optionalResourceLocations.isEmpty()) {
+ List textureAtlasSprites = new ArrayList();
+
+ for(ResourceLocation resourceLocation : optionalResourceLocations.get()) {
+ TextureAtlasSprite textureAtlasSprite1 = (TextureAtlasSprite)preparations.regions().get(resourceLocation);
+ if (textureAtlasSprite1 == null) {
+ set.add(resourceLocation);
+ textureAtlasSprites.add(textureAtlasSprite);
+ } else {
+ textureAtlasSprites.add(textureAtlasSprite1);
+ }
+ }
+
+ if (textureAtlasSprites.isEmpty()) {
+ textureAtlasSprites.add(textureAtlasSprite);
+ }
+
+ (this.spriteSets.get(particleDefinition.resourceLocation())).rebind(textureAtlasSprites);
+ }
+
+ });
+ if (!set.isEmpty()) {
+ PMWeather.LOGGER.warn("Missing particle sprites: {}", set.stream().sorted().map(ResourceLocation::toString).collect(Collectors.joining(",")));
+ }
+
+ profilerFiller1.pop();
+ profilerFiller1.endTick();
+ }, executor1);
+ }
+
+ private Optional> loadParticleDescription(ResourceLocation resourceLocation, Resource resource) {
+ if (!this.spriteSets.containsKey(resourceLocation)) {
+ PMWeather.LOGGER.debug("Redundant texture list for particle: {}", resourceLocation);
+ return Optional.>empty();
+ } else {
+ try (Reader reader = resource.openAsReader()) {
+ ParticleDescription particleDescription = ParticleDescription.fromJson(GsonHelper.parse(reader));
+ return Optional.>of(particleDescription.getTextures());
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to load description for particle " + String.valueOf((Object)resourceLocation), e);
+ }
+ }
+ }
+
+ @Nullable
+ private Particle makeParticle(T particleOptions, double x, double y, double z, double xMotion, double yMotion, double zMotion) {
+ ParticleProvider particleProvider = this.providers.get(BuiltInRegistries.PARTICLE_TYPE.getKey(particleOptions.getType()));
+ return particleProvider == null ? null : particleProvider.createParticle(particleOptions, this.level, x, y, z, xMotion, yMotion, zMotion);
+ }
+
+ public void add(Particle particle) {
+ Optional optional = particle.getParticleGroup();
+ if (optional.isPresent()) {
+ if (this.hasSpaceInParticleLimit(optional.get())) {
+ this.particlesToAdd.add(particle);
+ this.updateCount(optional.get(), 1);
+ }
+ } else {
+ this.particlesToAdd.add(particle);
+ }
+
+ }
+
+ public void tick() {
+ this.level.getProfiler().push("pmweather_particle_tick");
+ this.particles.forEach((particleRenderType, particles1) -> {
+ this.level.getProfiler().push("pmweather_particle_tick_" + particleRenderType.toString());
+ this.tickParticleList(particles1);
+ this.level.getProfiler().pop();
+ });
+ if (!this.trackingEmitters.isEmpty()) {
+ List list = Lists.newArrayList();
+
+ for(TrackingEmitter trackingEmitter : this.trackingEmitters) {
+ trackingEmitter.tick();
+ if (!trackingEmitter.isAlive()) {
+ list.add(trackingEmitter);
+ }
+ }
+
+ this.trackingEmitters.removeAll(list);
+ }
+
+ Particle particle;
+ if (!this.particlesToAdd.isEmpty()) {
+ while((particle = this.particlesToAdd.poll()) != null) {
+ (this.particles.computeIfAbsent(particle.getRenderType(), (particleRenderType) -> EvictingQueue.create(32768))).add(particle);
+ }
+ }
+
+ this.level.getProfiler().pop();
+ }
+
+ private void tickParticleList(Collection particles) {
+ if (!particles.isEmpty()) {
+ Iterator iterator = particles.iterator();
+
+ while(iterator.hasNext()) {
+ Particle particle = iterator.next();
+ this.tickParticle(particle);
+ if (!particle.isAlive()) {
+ particle.getParticleGroup().ifPresent((particleGroup) -> this.updateCount(particleGroup, -1));
+ iterator.remove();
+ }
+ }
+ }
+
+ }
+
+ private void updateCount(ParticleGroup particleGroup, int count) {
+ this.trackedParticleCounts.addTo(particleGroup, count);
+ }
+
+ private void tickParticle(Particle particle) {
+ try {
+ particle.tick();
+ } catch (Throwable throwable) {
+ CrashReport crashReport = CrashReport.forThrowable(throwable, "Ticking Particle");
+ CrashReportCategory crashReportCategory = crashReport.addCategory("Particle being ticked");
+ Objects.requireNonNull(particle);
+ crashReportCategory.setDetail("Particle", particle::toString);
+ ParticleRenderType var10002 = particle.getRenderType();
+ Objects.requireNonNull(var10002);
+ crashReportCategory.setDetail("Particle Type", var10002::toString);
+ throw new ReportedException(crashReport);
+ }
+ }
+
+ public void render(PoseStack poseStack, MultiBufferSource.BufferSource bufferSource, LightTexture lightTexture, Camera camera, float partialTicks, @Nullable Frustum frustum) {
+ this.level.getProfiler().push("pmweather_particle_render");
+ float fogStart = RenderSystem.getShaderFogStart();
+ float fogEnd = RenderSystem.getShaderFogEnd();
+ RenderSystem.setShaderFogStart(fogStart);
+ RenderSystem.setShaderFogEnd(fogEnd * 2.0F);
+ lightTexture.turnOnLightLayer();
+ RenderSystem.enableDepthTest();
+ RenderSystem.activeTexture(33986);
+ RenderSystem.activeTexture(33984);
+ Matrix4fStack matrix4fStack = RenderSystem.getModelViewStack();
+ matrix4fStack.pushMatrix();
+ matrix4fStack.mul(poseStack.last().pose());
+ RenderSystem.applyModelViewMatrix();
+ RenderSystem.disableCull();
+ int particleCount = 0;
+
+ for(ParticleRenderType particleRenderType : this.particles.keySet()) {
+ this.level.getProfiler().push(particleRenderType.toString());
+ if (particleRenderType != ParticleRenderType.NO_RENDER) {
+ Iterable iterable = this.particles.get(particleRenderType);
+ if (iterable != null) {
+ RenderSystem.setShader(GameRenderer::getParticleShader);
+ Tesselator tesselator = Tesselator.getInstance();
+ BufferBuilder bufferBuilder = particleRenderType.begin(tesselator, this.textureManager);
+ Map> sortedList = new HashMap>();
+ int maxRenderOrder = 0;
+
+ for(Particle particle : iterable) {
+ int renderOrder = 10;
+ if (particle instanceof EntityRotFX) {
+ EntityRotFX entityRotFX = (EntityRotFX)particle;
+ renderOrder = entityRotFX.renderOrder;
+ }
+
+ if (renderOrder > maxRenderOrder) {
+ maxRenderOrder = renderOrder;
+ }
+
+ if (sortedList.containsKey(renderOrder)) {
+ (sortedList.get(renderOrder)).add(particle);
+ } else {
+ List list = new ArrayList();
+ list.add(particle);
+ sortedList.put(renderOrder, list);
+ }
+ }
+
+ for(int i = 0; i <= maxRenderOrder; ++i) {
+ if (sortedList.containsKey(i)) {
+ List particlesSorted = sortedList.get(i);
+ particlesSorted.sort((p1, p2) -> {
+ double d1 = p1.getPos().distanceToSqr(camera.getPosition());
+ double d2 = p2.getPos().distanceToSqr(camera.getPosition());
+ return Double.compare(d2, d1);
+ });
+
+ for(Particle particle : particlesSorted) {
+ if (particle instanceof EntityRotFX) {
+ EntityRotFX entityRotFX = (EntityRotFX)particle;
+ if (camera.getPosition().distanceToSqr(particle.getPos()) > (double)(entityRotFX.renderRange * entityRotFX.renderRange) || frustum != null && !frustum.isVisible(entityRotFX.getBoundingBoxForRender())) {
+ continue;
+ }
+ } else if (camera.getPosition().distanceToSqr(particle.getPos()) > (double)65536.0F || frustum != null && !frustum.isVisible(particle.getRenderBoundingBox(partialTicks))) {
+ continue;
+ }
+
+ if (!(camera.getPosition().distanceToSqr(particle.getPos()) > (double)(ClientConfig.maxParticleSpawnDistanceFromPlayer * ClientConfig.maxParticleSpawnDistanceFromPlayer))) {
+ try {
+ particle.render(bufferBuilder, camera, partialTicks);
+ } catch (Throwable throwable) {
+ CrashReport crashReport = CrashReport.forThrowable(throwable, "Rendering Particle");
+ CrashReportCategory crashReportCategory = crashReport.addCategory("Particle being rendered");
+ Objects.requireNonNull(particle);
+ crashReportCategory.setDetail("Particle", particle::toString);
+ Objects.requireNonNull(particleRenderType);
+ crashReportCategory.setDetail("Particle Type", particleRenderType::toString);
+ throw new ReportedException(crashReport);
+ }
+ }
+ }
+ }
+ }
+
+ MeshData meshData = bufferBuilder.build();
+ if (meshData != null) {
+ BufferUploader.drawWithShader(meshData);
+ }
+ }
+
+ this.level.getProfiler().pop();
+ }
+ }
+
+ matrix4fStack.popMatrix();
+ RenderSystem.applyModelViewMatrix();
+ RenderSystem.depthMask(true);
+ RenderSystem.disableBlend();
+ lightTexture.turnOffLightLayer();
+ RenderSystem.setShaderFogStart(fogStart);
+ RenderSystem.setShaderFogEnd(fogEnd);
+ this.level.getProfiler().pop();
+ }
+
+ public void setLevel(@Nullable ClientLevel level) {
+ this.level = level;
+ this.clearParticles();
+ this.trackingEmitters.clear();
+ }
+
+ private boolean hasSpaceInParticleLimit(ParticleGroup particleGroup) {
+ return this.trackedParticleCounts.getInt(particleGroup) < particleGroup.getLimit();
+ }
+
+ public void clearParticles() {
+ this.particles.clear();
+ this.particlesToAdd.clear();
+ this.trackingEmitters.clear();
+ this.trackedParticleCounts.clear();
+ }
+
+ public Map> getParticles() {
+ return this.particles;
+ }
+
+ static {
+ RENDER_ORDER = ImmutableList.of(ParticleRenderType.TERRAIN_SHEET, ParticleRenderType.PARTICLE_SHEET_OPAQUE, ParticleRenderType.PARTICLE_SHEET_LIT, ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT, ParticleRenderType.CUSTOM, EntityRotFX.SORTED_OPAQUE_BLOCK, EntityRotFX.SORTED_TRANSLUCENT);
+ }
+
+ static class MutableSpriteSet implements SpriteSet {
+ private List sprites;
+
+ MutableSpriteSet() {
+ super();
+ }
+
+ public TextureAtlasSprite get(int i, int i1) {
+ return this.sprites.get(i * (this.sprites.size() - 1) / i1);
+ }
+
+ public TextureAtlasSprite get(RandomSource randomSource) {
+ return this.sprites.get(randomSource.nextInt(this.sprites.size()));
+ }
+
+ public void rebind(List textureAtlasSprites) {
+ this.sprites = ImmutableList.copyOf(textureAtlasSprites);
+ }
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleRegistry.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleRegistry.java
new file mode 100644
index 00000000..53298b0e
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleRegistry.java
@@ -0,0 +1,73 @@
+package dev.protomanly.pmweather.particle;
+
+import dev.protomanly.pmweather.PMWeather;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import net.minecraft.client.renderer.texture.TextureAtlas;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
+import net.minecraft.core.HolderLookup;
+import net.minecraft.data.PackOutput;
+import net.minecraft.resources.ResourceLocation;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.bus.api.SubscribeEvent;
+import net.neoforged.fml.common.EventBusSubscriber;
+import net.neoforged.fml.common.EventBusSubscriber.Bus;
+import net.neoforged.neoforge.client.event.TextureAtlasStitchedEvent;
+import net.neoforged.neoforge.common.data.ExistingFileHelper;
+import net.neoforged.neoforge.common.data.SpriteSourceProvider;
+
+public class ParticleRegistry extends SpriteSourceProvider {
+ public static TextureAtlasSprite rain;
+ public static TextureAtlasSprite mist;
+ public static TextureAtlasSprite splash;
+ public static TextureAtlasSprite snow;
+ public static TextureAtlasSprite snow1;
+ public static TextureAtlasSprite snow2;
+ public static TextureAtlasSprite snow3;
+ public static TextureAtlasSprite sleet;
+
+ public ParticleRegistry(PackOutput output, CompletableFuture lookupProvider, String modId, ExistingFileHelper existingFileHelper) {
+ super(output, lookupProvider, modId, existingFileHelper);
+ }
+
+ protected void gather() {
+ this.addSprite(PMWeather.getPath("particle/rain"));
+ this.addSprite(PMWeather.getPath("particle/mist"));
+ this.addSprite(PMWeather.getPath("particle/splash"));
+ this.addSprite(PMWeather.getPath("particle/snow"));
+ this.addSprite(PMWeather.getPath("particle/snow1"));
+ this.addSprite(PMWeather.getPath("particle/snow2"));
+ this.addSprite(PMWeather.getPath("particle/snow3"));
+ this.addSprite(PMWeather.getPath("particle/sleet"));
+ }
+
+ public void addSprite(ResourceLocation resourceLocation) {
+ this.atlas(SpriteSourceProvider.PARTICLES_ATLAS).addSource(new SingleFile(resourceLocation, Optional.empty()));
+ }
+
+ @EventBusSubscriber(
+ modid = "pmweather",
+ bus = Bus.MOD,
+ value = {Dist.CLIENT}
+ )
+ public static class Events {
+ public Events() {
+ super();
+ }
+
+ @SubscribeEvent
+ public static void getRegisteredParticles(TextureAtlasStitchedEvent event) {
+ if (event.getAtlas().location().equals(TextureAtlas.LOCATION_PARTICLES)) {
+ ParticleRegistry.rain = event.getAtlas().getSprite(PMWeather.getPath("particle/rain"));
+ ParticleRegistry.mist = event.getAtlas().getSprite(PMWeather.getPath("particle/mist"));
+ ParticleRegistry.snow = event.getAtlas().getSprite(PMWeather.getPath("particle/snow"));
+ ParticleRegistry.snow1 = event.getAtlas().getSprite(PMWeather.getPath("particle/snow1"));
+ ParticleRegistry.snow2 = event.getAtlas().getSprite(PMWeather.getPath("particle/snow2"));
+ ParticleRegistry.snow3 = event.getAtlas().getSprite(PMWeather.getPath("particle/snow3"));
+ ParticleRegistry.splash = event.getAtlas().getSprite(PMWeather.getPath("particle/splash"));
+ ParticleRegistry.sleet = event.getAtlas().getSprite(PMWeather.getPath("particle/sleet"));
+ }
+ }
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleTexExtraRender.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleTexExtraRender.java
new file mode 100644
index 00000000..6a80fe9e
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleTexExtraRender.java
@@ -0,0 +1,104 @@
+package dev.protomanly.pmweather.particle;
+
+import com.mojang.blaze3d.vertex.VertexConsumer;
+import com.mojang.math.Axis;
+import dev.protomanly.pmweather.util.Util;
+import net.minecraft.client.Camera;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.core.BlockPos;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.levelgen.Heightmap.Types;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Quaternionf;
+import org.joml.Vector3f;
+
+public class ParticleTexExtraRender extends ParticleTexFX {
+ public int extraParticlesBaseAmount = 5;
+ public boolean noExtraParticles = false;
+ public float extraRandomSecondaryYawRotation = 360.0F;
+
+ public ParticleTexExtraRender(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, TextureAtlasSprite sprite) {
+ super(level, x, y, z, xSpeed, ySpeed, zSpeed, sprite);
+ }
+
+ public void tickExtraRotations() {
+ if (this.slantParticleToWind) {
+ double speed = this.xd * this.xd + this.zd * this.zd;
+ this.rotationYaw = -((float)Math.toDegrees(Math.atan2(this.zd, this.xd))) - 90.0F;
+ this.rotationPitch = Math.min(45.0F, (float)(speed * (double)120.0F));
+ this.rotationPitch += (float)(this.entityID % 10 - 5);
+ }
+
+ }
+
+ public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) {
+ Vec3 vec3d = renderInfo.getPosition();
+ float f = (float)(Mth.lerp((double)partialTicks, this.xo, this.x) - vec3d.x());
+ float f1 = (float)(Mth.lerp((double)partialTicks, this.yo, this.y) - vec3d.y());
+ float f2 = (float)(Mth.lerp((double)partialTicks, this.zo, this.z) - vec3d.z());
+ Quaternionf quaternion;
+ if (!this.facePlayer && (this.rotationPitch != 0.0F || this.rotationYaw != 0.0F)) {
+ quaternion = new Quaternionf(0.0F, 0.0F, 0.0F, 1.0F);
+ quaternion.mul(Axis.YP.rotationDegrees(this.rotationYaw));
+ quaternion.mul(Axis.XP.rotationDegrees(this.rotationPitch));
+ if (this.extraRandomSecondaryYawRotation > 0.0F) {
+ quaternion.mul(Axis.YP.rotationDegrees((float)this.entityID % this.extraRandomSecondaryYawRotation));
+ }
+ } else {
+ quaternion = renderInfo.rotation();
+ }
+
+ float u0 = this.getU0();
+ float u1 = this.getU1();
+ float v0 = this.getV0();
+ float v1 = this.getV1();
+ int renderAmount;
+ if (this.noExtraParticles) {
+ renderAmount = 1;
+ } else {
+ renderAmount = Math.min(1 + this.extraParticlesBaseAmount, Util.MAX_RAIN_DROPS);
+ }
+
+ for(int ii = 0; ii < renderAmount; ++ii) {
+ double xx = (double)0.0F;
+ double zz = (double)0.0F;
+ double yy = (double)0.0F;
+ if (ii != 0) {
+ xx = Util.RAIN_POSITIONS[ii].x;
+ yy = Util.RAIN_POSITIONS[ii].y;
+ zz = Util.RAIN_POSITIONS[ii].z;
+ }
+
+ if (this.dontRenderUnderTopmostBlock) {
+ int height2 = this.level.getHeightmapPos(Types.MOTION_BLOCKING, new BlockPos((int)(this.x + xx), (int)this.y, (int)(this.z + zz))).getY();
+ if (this.y + yy < (double)height2) {
+ continue;
+ }
+ }
+
+ int i = this.getLightColor(partialTicks);
+ if (i > 0) {
+ this.lastNonZeroBrightness = i;
+ } else {
+ i = this.lastNonZeroBrightness;
+ }
+
+ Vector3f[] v3f = new Vector3f[]{new Vector3f(-1.0F, -1.0F, 0.0F), new Vector3f(-1.0F, 1.0F, 0.0F), new Vector3f(1.0F, 1.0F, 0.0F), new Vector3f(1.0F, -1.0F, 0.0F)};
+ float scale = this.getQuadSize(partialTicks);
+
+ for(int v = 0; v < 4; ++v) {
+ Vector3f vector3f = v3f[v];
+ vector3f.rotate(quaternion);
+ vector3f.mul(scale);
+ vector3f.add(f, f1, f2);
+ }
+
+ buffer.addVertex((float)(xx + (double)v3f[0].x), (float)(yy + (double)v3f[0].y), (float)(zz + (double)v3f[0].z)).setUv(u1, v1).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(i);
+ buffer.addVertex((float)(xx + (double)v3f[1].x), (float)(yy + (double)v3f[1].y), (float)(zz + (double)v3f[1].z)).setUv(u1, v0).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(i);
+ buffer.addVertex((float)(xx + (double)v3f[2].x), (float)(yy + (double)v3f[2].y), (float)(zz + (double)v3f[2].z)).setUv(u0, v0).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(i);
+ buffer.addVertex((float)(xx + (double)v3f[3].x), (float)(yy + (double)v3f[3].y), (float)(zz + (double)v3f[3].z)).setUv(u0, v1).setColor(this.rCol, this.gCol, this.bCol, this.alpha).setLight(i);
+ }
+
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleTexFX.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleTexFX.java
new file mode 100644
index 00000000..c3f912f8
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/ParticleTexFX.java
@@ -0,0 +1,16 @@
+package dev.protomanly.pmweather.particle;
+
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+
+public class ParticleTexFX extends EntityRotFX {
+ public ParticleTexFX(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, TextureAtlasSprite sprite) {
+ super(level, x, y, z, xSpeed, ySpeed - (double)0.5F, zSpeed);
+ this.setSprite(sprite);
+ this.setColor(1.0F, 1.0F, 1.0F);
+ this.gravity = 1.0F;
+ this.quadSize = 0.15F;
+ this.setLifetime(100);
+ this.setCanCollide(false);
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/behavior/ParticleBehavior.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/behavior/ParticleBehavior.java
new file mode 100644
index 00000000..fabb9ba6
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/particle/behavior/ParticleBehavior.java
@@ -0,0 +1,189 @@
+package dev.protomanly.pmweather.particle.behavior;
+
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.particle.EntityRotFX;
+import dev.protomanly.pmweather.particle.ParticleTexExtraRender;
+import java.util.ArrayList;
+import java.util.List;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.phys.Vec3;
+
+public class ParticleBehavior {
+ public List particles = new ArrayList();
+ public Vec3 coordSource;
+ public Entity sourceEntity;
+ public float rateDarken = 0.025F;
+ public float rateBrighten = 0.01F;
+ public float rateBrightenSlower = 0.003F;
+ public float rateAlpha = 0.002F;
+ public float rateScale = 0.1F;
+ public int tickSmokifyTrigger = 40;
+ float vanillaRainRed = 0.7F;
+ float vanillaRainGreen = 0.7F;
+ float vanillaRainBlue = 1.0F;
+
+ public ParticleBehavior(Vec3 coordSource) {
+ super();
+ this.coordSource = coordSource;
+ }
+
+ public EntityRotFX initParticle(EntityRotFX particle) {
+ particle.setPrevPosX(particle.getX());
+ particle.setPrevPosY(particle.getY());
+ particle.setPrevPosZ(particle.getZ());
+ particle.setSize(0.01F, 0.01F);
+ return particle;
+ }
+
+ public void initParticleRain(EntityRotFX particle, int extraRenderCount) {
+ if (particle instanceof ParticleTexExtraRender particleTexExtraRender) {
+ particleTexExtraRender.extraParticlesBaseAmount = extraRenderCount;
+ }
+
+ particle.killWhenUnderTopmostBlock = true;
+ particle.setCanCollide(false);
+ particle.killWhenUnderCameraAtLeast = 5;
+ particle.dontRenderUnderTopmostBlock = true;
+ particle.fastLight = true;
+ particle.slantParticleToWind = true;
+ particle.facePlayer = false;
+ particle.setScale(0.3F);
+ particle.isTransparent = true;
+ particle.setGravity(1.8F);
+ particle.setLifetime(50);
+ particle.ticksFadeInMax = 5.0F;
+ particle.ticksFadeOutMax = 5.0F;
+ particle.ticksFadeOutMaxOnDeath = 3.0F;
+ particle.setAlpha(0.0F);
+ particle.rotationYaw = (float)PMWeather.RANDOM.nextInt(360) - 180.0F;
+ particle.setMotionY((double)-0.5F);
+ particle.setColor(this.vanillaRainRed, this.vanillaRainGreen, this.vanillaRainBlue);
+ particle.spawnAsWeatherEffect();
+ }
+
+ public void initParticleGroundSplash(EntityRotFX particle) {
+ particle.killWhenUnderTopmostBlock = true;
+ particle.setCanCollide(false);
+ particle.killWhenUnderCameraAtLeast = 5;
+ particle.facePlayer = true;
+ particle.setScale(0.2F + PMWeather.RANDOM.nextFloat() * 0.05F);
+ particle.setLifetime(15);
+ particle.setGravity(1.0F);
+ particle.ticksFadeInMax = 3.0F;
+ particle.ticksFadeOutMax = 4.0F;
+ particle.setAlpha(0.0F);
+ particle.renderOrder = 2;
+ particle.rotationYaw = (float)PMWeather.RANDOM.nextInt(360) - 180.0F;
+ particle.rotationPitch = 90.0F;
+ particle.setMotionY((double)(PMWeather.RANDOM.nextFloat() * 0.2F));
+ particle.setMotionX((double)((PMWeather.RANDOM.nextFloat() - 0.5F) * 0.01F));
+ particle.setMotionZ((double)((PMWeather.RANDOM.nextFloat() - 0.5F) * 0.01F));
+ particle.setColor(this.vanillaRainRed, this.vanillaRainGreen, this.vanillaRainBlue);
+ }
+
+ public void initParticleSnow(EntityRotFX particle, int extraRenderCount, float windSpeed) {
+ if (particle instanceof ParticleTexExtraRender particleTexExtraRender) {
+ particleTexExtraRender.extraParticlesBaseAmount = extraRenderCount;
+ }
+
+ float windScale = Math.max(0.1F, 1.0F - windSpeed);
+ particle.setCanCollide(false);
+ particle.ticksFadeOutMaxOnDeath = 5.0F;
+ particle.dontRenderUnderTopmostBlock = true;
+ particle.killWhenUnderTopmostBlock = true;
+ particle.killWhenFarFromCameraAtLeast = 25;
+ particle.setMotionX((double)0.0F);
+ particle.setMotionY((double)0.0F);
+ particle.setMotionZ((double)0.0F);
+ particle.setScale(0.19500001F + PMWeather.RANDOM.nextFloat() * 0.05F);
+ particle.setGravity(0.05F + PMWeather.RANDOM.nextFloat() * 0.1F);
+ particle.setLifetime((int)(1440.0F * windScale));
+ particle.facePlayer = true;
+ particle.ticksFadeInMax = 40.0F * windScale;
+ particle.ticksFadeOutMax = 40.0F * windScale;
+ particle.ticksFadeOutMaxOnDeath = 10.0F;
+ particle.setAlpha(0.0F);
+ particle.rotationYaw = (float)PMWeather.RANDOM.nextInt(360) - 180.0F;
+ }
+
+ public void initParticleSleet(EntityRotFX particle, int extraRenderCount) {
+ if (particle instanceof ParticleTexExtraRender particleTexExtraRender) {
+ particleTexExtraRender.extraParticlesBaseAmount = extraRenderCount;
+ }
+
+ particle.setCanCollide(false);
+ particle.ticksFadeOutMaxOnDeath = 5.0F;
+ particle.dontRenderUnderTopmostBlock = true;
+ particle.killWhenFarFromCameraAtLeast = 25;
+ particle.setMotionX((double)0.0F);
+ particle.setMotionY((double)0.0F);
+ particle.setMotionZ((double)0.0F);
+ particle.setScale(0.3F);
+ particle.setGravity(1.2F);
+ particle.setLifetime(50);
+ particle.facePlayer = true;
+ particle.ticksFadeInMax = 5.0F;
+ particle.ticksFadeOutMax = 5.0F;
+ particle.ticksFadeOutMaxOnDeath = 10.0F;
+ particle.setAlpha(0.0F);
+ particle.rotationYaw = (float)PMWeather.RANDOM.nextInt(360) - 180.0F;
+ }
+
+ public void initParticleHail(EntityRotFX particle) {
+ particle.killWhenUnderTopmostBlock = false;
+ particle.setCanCollide(true);
+ particle.killOnCollide = true;
+ particle.killWhenUnderCameraAtLeast = 5;
+ particle.dontRenderUnderTopmostBlock = true;
+ particle.rotationYaw = (float)PMWeather.RANDOM.nextInt(360);
+ particle.rotationPitch = (float)PMWeather.RANDOM.nextInt(360);
+ particle.fastLight = true;
+ particle.slantParticleToWind = true;
+ particle.windWeight = 1.5F;
+ particle.ignoreWind = false;
+ particle.spinFast = true;
+ particle.spinFastRate = 10.0F;
+ particle.facePlayer = false;
+ particle.setScale(0.105000004F);
+ particle.isTransparent = false;
+ particle.setGravity(3.5F);
+ particle.ticksFadeInMax = 5.0F;
+ particle.ticksFadeOutMax = 5.0F;
+ particle.ticksFadeOutMaxOnDeath = 50.0F;
+ particle.fullAlphaTarget = 1.0F;
+ particle.setAlpha(0.0F);
+ particle.rotationYaw = (float)(PMWeather.RANDOM.nextInt(360) - 180);
+ particle.setMotionY((double)-0.5F);
+ particle.setColor(0.9F, 0.9F, 0.9F);
+ particle.bounceOnVerticalImpact = true;
+ particle.bounceOnVerticalImpactEnergy = 0.3F;
+ }
+
+ public void initParticleCube(EntityRotFX particle) {
+ particle.killWhenUnderTopmostBlock = false;
+ particle.setCanCollide(true);
+ particle.killOnCollide = true;
+ particle.killOnCollideActivateAtAge = 30;
+ particle.killWhenUnderCameraAtLeast = 0;
+ particle.dontRenderUnderTopmostBlock = true;
+ particle.rotationPitch = (float)PMWeather.RANDOM.nextInt(360);
+ particle.fastLight = true;
+ particle.ignoreWind = true;
+ particle.spinFast = true;
+ particle.spinFastRate = 1.0F;
+ particle.facePlayer = false;
+ particle.setScale(0.45F);
+ particle.isTransparent = false;
+ particle.setGravity(0.5F);
+ particle.setLifetime(400);
+ particle.ticksFadeInMax = 5.0F;
+ particle.ticksFadeOutMax = 5.0F;
+ particle.ticksFadeOutMaxOnDeath = 20.0F;
+ particle.fullAlphaTarget = 1.0F;
+ particle.setAlpha(0.0F);
+ particle.rotationYaw = (float)(PMWeather.RANDOM.nextInt(360) - 180);
+ particle.vanillaMotionDampen = true;
+ particle.bounceOnVerticalImpact = true;
+ particle.bounceOnVerticalImpactEnergy = 0.3F;
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/render/RenderEvents.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/render/RenderEvents.java
new file mode 100644
index 00000000..c47e98c2
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/render/RenderEvents.java
@@ -0,0 +1,72 @@
+package dev.protomanly.pmweather.render;
+
+import dev.protomanly.pmweather.event.GameBusClientEvents;
+import dev.protomanly.pmweather.particle.ParticleManager;
+import dev.protomanly.pmweather.shaders.ModShaders;
+import dev.protomanly.pmweather.weather.Lightning;
+import dev.protomanly.pmweather.weather.WeatherHandlerClient;
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.bus.api.SubscribeEvent;
+import net.neoforged.fml.common.EventBusSubscriber;
+import net.neoforged.fml.common.EventBusSubscriber.Bus;
+import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
+import net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage;
+
+@EventBusSubscriber(
+ modid = "pmweather",
+ bus = Bus.GAME,
+ value = {Dist.CLIENT}
+)
+public class RenderEvents {
+ public static List lightningColors = new ArrayList() {
+ {
+ this.add(new Color(16777215));
+ this.add(new Color(15587698));
+ this.add(new Color(13041578));
+ this.add(new Color(10778102));
+ this.add(new Color(15106690));
+ this.add(new Color(7203548));
+ }
+ };
+
+ public RenderEvents() {
+ super();
+ }
+
+ @SubscribeEvent
+ public static void render(RenderLevelStageEvent event) {
+ if (event.getStage() == Stage.AFTER_WEATHER) {
+ RadarRenderer.RenderedRadars = 0;
+ float partialTicks = event.getPartialTick().getGameTimeDeltaTicks();
+ WeatherHandlerClient weatherHandlerClient = (WeatherHandlerClient)GameBusClientEvents.weatherHandler;
+ if (weatherHandlerClient != null) {
+ List lightnings = weatherHandlerClient.lightnings;
+
+ for(int i = 0; i < lightnings.size(); ++i) {
+ Lightning lightning = lightnings.get(i);
+ if (lightning != null) {
+ Random rand = new Random(lightning.seed);
+ Color color = lightningColors.get(rand.nextInt(lightningColors.size()));
+ float p = Math.clamp(((float)lightning.ticks + partialTicks) / (float)lightning.lifetime, 0.0F, 1.0F);
+ float alpha = (float)Math.abs(Math.cos(Math.sqrt((double)p) * Math.PI * (double)3.0F)) * (1.0F - p);
+ color = new Color(color.getRed(), color.getGreen(), color.getBlue(), Math.clamp((long)((int)(alpha * 255.0F)), 0, 255));
+ CustomLightningRenderer.render(lightning.position, lightning.seed, event.getCamera(), color);
+ }
+ }
+
+ ModShaders.renderShaders(event.getPartialTick().getGameTimeDeltaTicks(), event.getCamera(), event.getProjectionMatrix(), event.getModelViewMatrix());
+ ParticleManager pm = GameBusClientEvents.particleManager;
+ if (pm != null) {
+ pm.render(event.getPoseStack(), (MultiBufferSource.BufferSource)null, Minecraft.getInstance().gameRenderer.lightTexture(), event.getCamera(), event.getPartialTick().getGameTimeDeltaPartialTick(false), event.getFrustum());
+ }
+ }
+ }
+
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/shaders/ModShaders.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/shaders/ModShaders.java
new file mode 100644
index 00000000..75756f27
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/shaders/ModShaders.java
@@ -0,0 +1,347 @@
+package dev.protomanly.pmweather.shaders;
+
+import com.google.gson.JsonSyntaxException;
+import com.mojang.blaze3d.Blaze3D;
+import com.mojang.blaze3d.systems.RenderSystem;
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.compat.DistantHorizons;
+import dev.protomanly.pmweather.config.ClientConfig;
+import dev.protomanly.pmweather.config.ServerConfig;
+import dev.protomanly.pmweather.event.GameBusClientEvents;
+import dev.protomanly.pmweather.mixin.PostChainMixin;
+import dev.protomanly.pmweather.weather.Lightning;
+import dev.protomanly.pmweather.weather.Storm;
+import dev.protomanly.pmweather.weather.ThermodynamicEngine;
+import dev.protomanly.pmweather.weather.WeatherHandler;
+import dev.protomanly.pmweather.weather.WeatherHandlerClient;
+import dev.protomanly.pmweather.weather.WindEngine;
+import java.io.IOException;
+import java.util.List;
+import net.minecraft.client.Camera;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.player.LocalPlayer;
+import net.minecraft.client.renderer.EffectInstance;
+import net.minecraft.client.renderer.PostChain;
+import net.minecraft.client.renderer.PostPass;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.Mth;
+import net.minecraft.world.phys.Vec2;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Matrix4f;
+import org.lwjgl.opengl.GL32;
+
+public class ModShaders {
+ private static PostChain clouds;
+ private static Vec2 lastScroll;
+ public static Vec2 scroll;
+ private static int lastWidth;
+ private static int lastHeight;
+ private static float snow;
+ private static float lastSnow;
+ private static float gameTime;
+ private static boolean dhIntegrationInitialized;
+ private static Integer noiseTextureID;
+
+ public ModShaders() {
+ super();
+ }
+
+ public static void tick() {
+ Minecraft minecraft = Minecraft.getInstance();
+ LocalPlayer player = minecraft.player;
+ WeatherHandler weatherHandler = GameBusClientEvents.weatherHandler;
+ if (player != null && weatherHandler != null && Minecraft.getInstance().level != null) {
+ Vec3 wind = WindEngine.getWind((Vec3)(new Vec3(player.position().x, (double)minecraft.level.getMaxBuildHeight(), player.position().z)), minecraft.level, true, true, false);
+ Vec3 wind2 = wind.multiply(0.03, 0.03, 0.03);
+ lastScroll = scroll;
+ scroll = scroll.add(new Vec2(-((float)wind2.x), -((float)wind2.z)));
+
+ for(Storm storm : weatherHandler.getStorms()) {
+ if (storm.lastPosition == null) {
+ storm.lastPosition = storm.position;
+ } else {
+ storm.lastPosition = storm.lastPosition.lerp(storm.position, (double)0.05F);
+ }
+
+ storm.lastSpin = storm.spin;
+ storm.spin += storm.smoothWindspeed * 0.01F / Math.max(storm.smoothWidth, 20.0F);
+ }
+
+ ThermodynamicEngine.Precipitation precip = ThermodynamicEngine.getPrecipitationType(weatherHandler, player.position(), minecraft.level, 0);
+ lastSnow = snow;
+ if (precip != ThermodynamicEngine.Precipitation.SNOW && precip != ThermodynamicEngine.Precipitation.WINTRY_MIX) {
+ snow = Mth.lerp(0.05F, snow, 0.0F);
+ } else {
+ float rain = weatherHandler.getPrecipitation(player.position());
+ float snowBlindness = (float)Math.clamp(Math.pow(wind.length() / (double)60.0F, (double)2.0F) * (double)rain, (double)0.0F, (double)1.0F);
+ snow = Mth.lerp(0.05F, snow, snowBlindness);
+ }
+ }
+
+ }
+
+ public static void renderShaders(float partialTicks, Camera camera, Matrix4f projMat, Matrix4f modelMat) {
+ Minecraft minecraft = Minecraft.getInstance();
+ LocalPlayer player = minecraft.player;
+ WeatherHandler weatherHandler = GameBusClientEvents.weatherHandler;
+ if (clouds != null && player != null && weatherHandler != null && Minecraft.getInstance().level != null && ServerConfig.validDimensions != null && ServerConfig.validDimensions.contains(Minecraft.getInstance().level.dimension())) {
+ int width = minecraft.getWindow().getWidth();
+ int height = minecraft.getWindow().getHeight();
+ if (width != lastWidth || height != lastHeight) {
+ lastWidth = width;
+ lastHeight = height;
+ updateShaderGroupSize(clouds);
+ }
+
+ float gameTime = (float)Blaze3D.getTime();
+ RenderSystem.enableDepthTest();
+ RenderSystem.resetTextureMatrix();
+ RenderSystem.disableBlend();
+ RenderSystem.depthMask(false);
+ PostChain var11 = clouds;
+ if (var11 instanceof PostChainMixin) {
+ PostChainMixin data = (PostChainMixin)var11;
+ List passes = data.getPasses();
+ EffectInstance effect = ((PostPass)passes.getFirst()).getEffect();
+ effect.safeGetUniform("OutSize").set((float)width, (float)height);
+ Vec3 camPos = camera.getPosition();
+ effect.safeGetUniform("pos").set((float)camPos.x, (float)camPos.y, (float)camPos.z);
+ effect.safeGetUniform("scroll").set(Mth.lerp(partialTicks, lastScroll.x, scroll.x), Mth.lerp(partialTicks, lastScroll.y, scroll.y));
+ effect.safeGetUniform("maxSteps").set(800);
+ effect.safeGetUniform("stepSize").set(0.01F);
+ effect.safeGetUniform("fogStart").set(RenderSystem.getShaderFogStart() * 4.0F);
+ effect.safeGetUniform("fogEnd").set(RenderSystem.getShaderFogEnd() * 4.0F);
+ effect.safeGetUniform("proj").set(projMat.invert());
+ effect.safeGetUniform("viewmat").set(modelMat.invert());
+ effect.safeGetUniform("time").set((float)player.tickCount + partialTicks);
+ long seed = 0L;
+ if (GameBusClientEvents.weatherHandler != null) {
+ seed = GameBusClientEvents.weatherHandler.seed;
+ }
+
+ float gameTimeGoal = (float)player.level().getGameTime() + (float)seed / 1.0E14F + partialTicks;
+ gameTime = Mth.lerp(0.3F, gameTime, gameTimeGoal);
+ if (Math.abs(gameTime - gameTimeGoal) > 30.0F) {
+ gameTime = gameTimeGoal;
+ }
+
+ effect.safeGetUniform("worldTime").set(gameTime);
+ effect.safeGetUniform("layer0height").set((float)ServerConfig.layer0Height);
+ effect.safeGetUniform("layerCheight").set((float)ServerConfig.layerCHeight);
+ effect.safeGetUniform("stormSize").set((float)ServerConfig.stormSize * 2.0F);
+ float sunAng = Minecraft.getInstance().level.getSunAngle(partialTicks);
+ Vec3 sunDir = new Vec3(-Math.sin((double)sunAng), Math.cos((double)sunAng), (double)0.0F);
+ effect.safeGetUniform("sunDir").set((float)sunDir.x, (float)sunDir.y, (float)sunDir.z);
+ effect.safeGetUniform("lightIntensity").set((float)Math.pow((Math.cos((double)sunAng) + (double)1.0F) / (double)2.0F, (double)3.0F));
+ effect.safeGetUniform("downsample").set((float)ClientConfig.volumetricsDownsample);
+ ((PostPass)passes.get(1)).getEffect().safeGetUniform("downsample").set((float)ClientConfig.volumetricsDownsample);
+ ((PostPass)passes.get(1)).getEffect().safeGetUniform("glowFix").set(ClientConfig.glowFix ? 1.0F : 0.0F);
+ ((PostPass)passes.get(1)).getEffect().safeGetUniform("doBlur").set(ClientConfig.volumetricsBlur ? 1.0F : 0.0F);
+ effect.safeGetUniform("simpleLighting").set(ClientConfig.simpleLighting ? 0.0F : 1.0F);
+
+ try {
+ if (DistantHorizons.isAvailable()) {
+ int depthTextureId = DistantHorizons.getDepthTextureId();
+ GL32.glActiveTexture(33991);
+ GL32.glBindTexture(3553, depthTextureId);
+ effect.safeGetUniform("dhDepthTex0").set(7);
+ effect.safeGetUniform("hasDHDepth").set(1.0F);
+ Matrix4f dhProj = DistantHorizons.getDhProjectionMatrix();
+ Matrix4f dhProjInv = (new Matrix4f(dhProj)).invert();
+ effect.safeGetUniform("dhProjection").set(dhProj);
+ effect.safeGetUniform("dhProjectionInverse").set(dhProjInv);
+ effect.safeGetUniform("dhViewmat").set(DistantHorizons.getDhModelViewMatrix());
+ effect.safeGetUniform("dhNearPlane").set(DistantHorizons.getNearPlane());
+ effect.safeGetUniform("dhFarPlane").set(DistantHorizons.getFarPlane());
+ effect.safeGetUniform("dhRenderDistance").set((float)DistantHorizons.getChunkRenderDistance() * 16.0F);
+ GL32.glActiveTexture(33984);
+ } else {
+ effect.safeGetUniform("hasDHDepth").set(0.0F);
+ }
+ } catch (Exception var39) {
+ PMWeather.LOGGER.debug("DH Uniforms not available");
+ effect.safeGetUniform("hasDHDepth").set(0.0F);
+ }
+
+ if (weatherHandler instanceof WeatherHandlerClient) {
+ WeatherHandlerClient whc = (WeatherHandlerClient)weatherHandler;
+ effect.safeGetUniform("rain").set(whc.getPrecipitation());
+ effect.safeGetUniform("snow").set(Mth.lerp(partialTicks, lastSnow, snow));
+ }
+
+ List storms = weatherHandler.getStorms();
+ float[] stormPositions = new float[48];
+ float[] stormVelocites = new float[32];
+ float[] stormStages = new float[16];
+ float[] stormEnergies = new float[16];
+ float[] stormTypes = new float[16];
+ float[] tornadoWindspeeds = new float[16];
+ float[] tornadoWidths = new float[16];
+ float[] tornadoTouchdownSpeeds = new float[16];
+ float[] visualOnlys = new float[16];
+ float[] stormSpins = new float[16];
+ float[] stormDyings = new float[16];
+ float[] tornadoShapes = new float[16];
+ float[] stormOcclusions = new float[16];
+ float[] lightningStrikes = new float[192];
+ if (GameBusClientEvents.weatherHandler != null) {
+ float[] lightningBrightness = new float[64];
+ List lightnings = ((WeatherHandlerClient)GameBusClientEvents.weatherHandler).lightnings;
+
+ for(int i = 0; i < lightnings.size(); ++i) {
+ if (i < 64) {
+ Lightning lightning = lightnings.get(i);
+ lightningStrikes[i * 3] = (float)lightning.position.x;
+ lightningStrikes[i * 3 + 1] = (float)lightning.position.y;
+ lightningStrikes[i * 3 + 2] = (float)lightning.position.z;
+ float p = Math.clamp(((float)lightning.ticks + partialTicks) / (float)lightning.lifetime, 0.0F, 1.0F);
+ lightningBrightness[i] = (float)Math.abs(Math.cos(Math.sqrt((double)p) * Math.PI * (double)3.0F)) * (1.0F - p);
+ }
+ }
+
+ effect.safeGetUniform("lightningStrikes").set(lightningStrikes);
+ effect.safeGetUniform("lightningCount").set(lightnings.size());
+ effect.safeGetUniform("lightningBrightness").set(lightningBrightness);
+ }
+
+ int count = 0;
+
+ for(int i = 0; i < storms.size(); ++i) {
+ if (i < 16) {
+ Storm storm = storms.get(i);
+ if (!(storm.position.multiply((double)1.0F, (double)0.0F, (double)1.0F).distanceTo(camera.getPosition().multiply((double)1.0F, (double)0.0F, (double)1.0F)) > (double)32000.0F) && (storm.stage > 0 || storm.energy > 0 || storm.stormType == 2) && storm.lastPosition != null) {
+ Vec3 pos = storm.lastPosition;
+ stormPositions[count * 3] = (float)pos.x;
+ stormPositions[count * 3 + 1] = (float)pos.y;
+ stormPositions[count * 3 + 2] = (float)pos.z;
+ Vec3 vel = storm.velocity;
+ stormVelocites[count * 2] = (float)vel.x;
+ stormVelocites[count * 2 + 1] = (float)vel.z;
+ stormStages[count] = (float)storm.stage;
+ stormEnergies[count] = (float)storm.energy;
+ tornadoWindspeeds[count] = storm.smoothWindspeed;
+ tornadoWidths[count] = storm.smoothWidth;
+ tornadoTouchdownSpeeds[count] = (float)storm.touchdownSpeed;
+ stormSpins[count] = Mth.lerp(partialTicks, storm.lastSpin, storm.spin);
+ tornadoShapes[count] = storm.tornadoShape;
+ stormTypes[count] = (float)storm.stormType;
+ stormOcclusions[count] = storm.occlusion;
+ visualOnlys[count] = storm.visualOnly ? 1.0F : -1.0F;
+ stormDyings[count] = storm.isDying ? 1.0F : -1.0F;
+ ++count;
+ }
+ }
+ }
+
+ effect.safeGetUniform("stormCount").set(count);
+ effect.safeGetUniform("stormPositions").set(stormPositions);
+ effect.safeGetUniform("stormVelocities").set(stormVelocites);
+ effect.safeGetUniform("stormStages").set(stormStages);
+ effect.safeGetUniform("stormEnergies").set(stormEnergies);
+ effect.safeGetUniform("stormTypes").set(stormTypes);
+ effect.safeGetUniform("tornadoWindspeeds").set(tornadoWindspeeds);
+ effect.safeGetUniform("tornadoWidths").set(tornadoWidths);
+ effect.safeGetUniform("tornadoTouchdownSpeeds").set(tornadoTouchdownSpeeds);
+ effect.safeGetUniform("visualOnlys").set(visualOnlys);
+ effect.safeGetUniform("stormSpins").set(stormSpins);
+ effect.safeGetUniform("stormDyings").set(stormDyings);
+ effect.safeGetUniform("tornadoShapes").set(tornadoShapes);
+ effect.safeGetUniform("stormOcclusions").set(stormOcclusions);
+ effect.safeGetUniform("overcastPerc").set((float)ServerConfig.overcastPercent);
+ effect.safeGetUniform("rainStrength").set((float)ServerConfig.rainStrength);
+ Vec3 sampPos = camera.getPosition().multiply((double)1.0F, (double)0.0F, (double)1.0F).add((double)0.0F, ServerConfig.layer0Height, (double)0.0F);
+ Vec3 lightingColor = new Vec3((double)1.0F, (double)1.0F, (double)1.0F);
+ lightingColor = lightingColor.lerp(new Vec3(0.741, 0.318, 0.227), Math.pow((double)1.0F - sunDir.y, (double)2.5F));
+ lightingColor = lightingColor.lerp(new Vec3(0.314, 0.408, 0.525), Math.clamp((sunDir.y + 0.1) / -0.1, (double)0.0F, (double)1.0F));
+ Vec3 skyColor = Minecraft.getInstance().level.getSkyColor(sampPos, partialTicks);
+ effect.safeGetUniform("lightingColor").set((float)lightingColor.x, (float)lightingColor.y, (float)lightingColor.z);
+ effect.safeGetUniform("skyColor").set((float)skyColor.x, (float)skyColor.y, (float)skyColor.z);
+ byte var10000;
+ switch (ClientConfig.volumetricsQuality.ordinal()) {
+ case 0 -> var10000 = 0;
+ case 1 -> var10000 = 1;
+ case 2 -> var10000 = 2;
+ case 3 -> var10000 = 3;
+ case 4 -> var10000 = 4;
+ default -> throw new MatchException((String)null, (Throwable)null);
+ }
+
+ int quality = var10000;
+ effect.safeGetUniform("quality").set(quality);
+ effect.safeGetUniform("nearPlane").set(0.05F);
+ effect.safeGetUniform("farPlane").set((float)(Integer)Minecraft.getInstance().options.renderDistance().get() * 4.0F * 16.0F);
+ effect.safeGetUniform("renderDistance").set(6000.0F);
+ clouds.process(partialTicks);
+ }
+
+ minecraft.getMainRenderTarget().bindWrite(false);
+ RenderSystem.depthMask(true);
+ projMat.invert();
+ modelMat.invert();
+ }
+
+ }
+
+ public static PostChain createShader(ResourceLocation resourceLocation) {
+ try {
+ Minecraft minecraft = Minecraft.getInstance();
+ return new PostChain(minecraft.getTextureManager(), minecraft.getResourceManager(), minecraft.getMainRenderTarget(), resourceLocation);
+ } catch (IOException e) {
+ PMWeather.LOGGER.error("Failed to load shader: {}", resourceLocation, e);
+ } catch (JsonSyntaxException e) {
+ PMWeather.LOGGER.error("Failed to parse shader: {}", resourceLocation, e);
+ }
+
+ return null;
+ }
+
+ public static void createShaders() {
+ if (clouds == null) {
+ clouds = createShader(PMWeather.getPath("shaders/post/clouds.json"));
+ }
+
+ }
+
+ public static void reload() {
+ if (clouds != null) {
+ clouds.close();
+ }
+
+ clouds = null;
+ createShaders();
+ updateShaderGroupSize(clouds);
+ PMWeather.LOGGER.info("Loaded PMWeather Shaders");
+ }
+
+ private static void updateShaderGroupSize(PostChain shaderGroup) {
+ if (shaderGroup != null) {
+ Minecraft minecraft = Minecraft.getInstance();
+ int width = minecraft.getWindow().getWidth();
+ int height = minecraft.getWindow().getHeight();
+ shaderGroup.resize(width, height);
+ }
+
+ }
+
+ static {
+ lastScroll = Vec2.ZERO;
+ scroll = Vec2.ZERO;
+ lastWidth = 0;
+ lastHeight = 0;
+ snow = 0.0F;
+ lastSnow = 0.0F;
+ gameTime = 0.0F;
+ dhIntegrationInitialized = false;
+ }
+
+ public static enum Quality {
+ POTATO,
+ LOW,
+ MEDIUM,
+ HIGH,
+ PC_KILLER;
+
+ private Quality() {
+ }
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/Storm.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/Storm.java
new file mode 100644
index 00000000..d47e1d42
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/Storm.java
@@ -0,0 +1,1119 @@
+package dev.protomanly.pmweather.weather;
+
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.block.ModBlocks;
+import dev.protomanly.pmweather.block.entity.RadarBlockEntity;
+import dev.protomanly.pmweather.config.Config;
+import dev.protomanly.pmweather.config.ServerConfig;
+import dev.protomanly.pmweather.entity.ModEntities;
+import dev.protomanly.pmweather.entity.MovingBlock;
+import dev.protomanly.pmweather.interfaces.ParticleData;
+import dev.protomanly.pmweather.particle.EntityRotFX;
+import dev.protomanly.pmweather.sound.ModSounds;
+import dev.protomanly.pmweather.sound.MovingSoundStreamingSource;
+import dev.protomanly.pmweather.util.CachedNBTTagCompound;
+import dev.protomanly.pmweather.util.Util;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.particle.Particle;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.core.SectionPos;
+import net.minecraft.core.Vec3i;
+import net.minecraft.core.Direction.Axis;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.sounds.SoundEvent;
+import net.minecraft.sounds.SoundEvents;
+import net.minecraft.sounds.SoundSource;
+import net.minecraft.tags.BlockTags;
+import net.minecraft.tags.TagKey;
+import net.minecraft.util.Mth;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.BlockStateProperties;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.levelgen.LegacyRandomSource;
+import net.minecraft.world.level.levelgen.Heightmap.Types;
+import net.minecraft.world.level.levelgen.synth.SimplexNoise;
+import net.minecraft.world.phys.AABB;
+import net.minecraft.world.phys.Vec2;
+import net.minecraft.world.phys.Vec3;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.api.distmarker.OnlyIn;
+import net.neoforged.fml.LogicalSide;
+import net.neoforged.fml.util.thread.EffectiveSide;
+import net.neoforged.neoforge.common.Tags.Blocks;
+
+public class Storm {
+ public static long LastUsedStormID = 0L;
+ private static final float resistance = 0.985F;
+ public static final float tickConversion = 0.05F;
+ @OnlyIn(Dist.CLIENT)
+ public MovingSoundStreamingSource tornadicWind;
+ @OnlyIn(Dist.CLIENT)
+ public MovingSoundStreamingSource tornadicDamage;
+ @OnlyIn(Dist.CLIENT)
+ public MovingSoundStreamingSource supercellWind;
+ @OnlyIn(Dist.CLIENT)
+ public MovingSoundStreamingSource eyewallWind;
+ @OnlyIn(Dist.CLIENT)
+ public MovingSoundStreamingSource undergroundWind;
+ public long ID;
+ public WeatherHandler weatherHandler;
+ public Vec3 position;
+ public Vec3 lastPosition;
+ public Vec3 velocity;
+ public int windspeed;
+ public float cycloneWindspeed = 0.0F;
+ public float smoothWindspeed = 0.0F;
+ public float width = 15.0F;
+ public float smoothWidth = 15.0F;
+ public float tornadoShape;
+ public float spin;
+ public float lastSpin;
+ public int energy;
+ public int stormType;
+ public int stage;
+ public int tickCount;
+ public int tornadoOnGroundTicks;
+ public boolean dead;
+ public Level level;
+ private final CachedNBTTagCompound nbtCache;
+ public SimplexNoise simplexNoise;
+ public float rankineFactor;
+ public List listParticleDebris;
+ private final List forceLoadedChunks;
+ public int maxStage;
+ public int maxProgress;
+ public boolean isDying;
+ public int growthSpeed;
+ public int maxWindspeed;
+ public int maxWidth;
+ public int ticksSinceDying;
+ public int touchdownSpeed;
+ public boolean onWater;
+ public float occlusion;
+ public boolean visualOnly;
+ public boolean cirus;
+ public boolean aimedAtPlayer;
+ public int maxColdEnergy;
+ public int coldEnergy;
+ public List vorticies;
+
+ public double FBM(Vec3 pos, int octaves, float lacunarity, float gain, float amplitude) {
+ double y = (double)0.0F;
+
+ for(int i = 0; i < Math.max(octaves, 1); ++i) {
+ y += (double)amplitude * this.simplexNoise.getValue(pos.x, pos.y, pos.z);
+ pos = pos.multiply((double)lacunarity, (double)lacunarity, (double)lacunarity);
+ amplitude *= gain;
+ }
+
+ return y;
+ }
+
+ public Vec3 rotateV3(Vec3 x, double angle) {
+ double rx = x.x * Math.cos(angle) - x.z * Math.sin(angle);
+ double rz = x.x * Math.sin(angle) + x.z * Math.cos(angle);
+ return new Vec3(rx, x.y, rz);
+ }
+
+ public Storm(WeatherHandler weatherHandler, Level level, @Nullable Float risk, int stormType) {
+ super();
+ this.tornadoShape = PMWeather.RANDOM.nextFloat() * 10.0F + 6.0F;
+ this.spin = 0.0F;
+ this.lastSpin = 0.0F;
+ this.tickCount = 0;
+ this.tornadoOnGroundTicks = 0;
+ this.dead = false;
+ this.rankineFactor = 4.5F;
+ this.forceLoadedChunks = new ArrayList();
+ this.maxStage = 0;
+ this.maxProgress = 0;
+ this.isDying = false;
+ this.growthSpeed = 20;
+ this.maxWindspeed = 0;
+ this.maxWidth = 15;
+ this.ticksSinceDying = 0;
+ this.touchdownSpeed = PMWeather.RANDOM.nextInt(65, 120);
+ this.onWater = false;
+ this.occlusion = 0.0F;
+ this.visualOnly = false;
+ this.cirus = false;
+ this.aimedAtPlayer = false;
+ this.maxColdEnergy = 300;
+ this.coldEnergy = 0;
+ this.vorticies = new ArrayList();
+ this.weatherHandler = weatherHandler;
+ this.level = level;
+ this.stormType = stormType;
+ this.simplexNoise = new SimplexNoise(new LegacyRandomSource(weatherHandler.seed));
+ this.nbtCache = new CachedNBTTagCompound();
+ if (level.isClientSide()) {
+ this.listParticleDebris = new ArrayList();
+ } else {
+ this.maxStage = 0;
+ this.maxProgress = PMWeather.RANDOM.nextInt(25, 99);
+ float stage1Chance = 1.0F / (float)ServerConfig.chanceInOneStage1;
+ float stage2Chance = 1.0F / (float)ServerConfig.chanceInOneStage2;
+ float stage3Chance = 1.0F / (float)ServerConfig.chanceInOneStage3;
+ if (risk != null && ServerConfig.environmentSystem && stormType == 0) {
+ stage1Chance *= risk * 1.75F + 0.05F;
+ stage2Chance *= risk;
+ stage3Chance *= risk * 0.75F;
+ PMWeather.LOGGER.debug("Readjusted stage chances: 1: {} 2: {} 3: {}", new Object[]{stage1Chance, stage2Chance, stage3Chance});
+ }
+
+ if (PMWeather.RANDOM.nextFloat() <= stage1Chance) {
+ this.maxStage = 1;
+ }
+
+ if (PMWeather.RANDOM.nextFloat() <= stage2Chance) {
+ this.maxStage = 2;
+ }
+
+ if (PMWeather.RANDOM.nextFloat() <= stage3Chance) {
+ this.maxStage = 3;
+ }
+
+ if (this.maxStage == 3 && stormType == 0) {
+ this.maxProgress = 100;
+ float mW;
+ if (risk != null && ServerConfig.environmentSystem) {
+ mW = risk * 80.0F;
+ } else {
+ mW = 125.0F;
+ }
+
+ mW += 55.0F;
+ this.maxWindspeed = Math.min((int)Mth.lerp(PMWeather.RANDOM.nextFloat(), 55.0F, mW), 220);
+ this.touchdownSpeed = PMWeather.RANDOM.nextInt(75, Math.max(25 + (int)((float)this.maxWindspeed * 1.1F), 100));
+ }
+
+ this.growthSpeed = PMWeather.RANDOM.nextInt(30, 80);
+ if (stormType == 1) {
+ this.growthSpeed = PMWeather.RANDOM.nextInt(40, 70);
+ }
+
+ this.maxWidth = PMWeather.RANDOM.nextInt(15, 25 + (int)(Math.pow((double)((float)this.maxWindspeed / 220.0F), (double)1.75F) * (ServerConfig.maxTornadoWidth - (double)25.0F)));
+ PMWeather.LOGGER.debug("Max Stage: {}, Max Energy: {}, Max Windspeed: {}, Max Width: {}, Touchdown Speed: {}", new Object[]{this.maxStage, this.maxProgress, this.maxWindspeed, this.maxWidth, this.touchdownSpeed});
+ }
+
+ }
+
+ public void recalc(@Nullable Float risk) {
+ if (this.maxStage == 3 && this.stormType == 0) {
+ this.maxProgress = 100;
+ float mW;
+ if (risk != null && ServerConfig.environmentSystem) {
+ mW = risk * 80.0F;
+ } else {
+ mW = 125.0F;
+ }
+
+ mW += 55.0F;
+ this.maxWindspeed = Math.min((int)Mth.lerp(PMWeather.RANDOM.nextFloat(), 55.0F, mW), 220);
+ this.touchdownSpeed = PMWeather.RANDOM.nextInt(75, Math.max(25 + (int)((float)this.maxWindspeed * 1.1F), 100));
+ }
+
+ this.growthSpeed = PMWeather.RANDOM.nextInt(30, 80);
+ if (this.stormType == 1) {
+ this.growthSpeed = PMWeather.RANDOM.nextInt(40, 70);
+ }
+
+ this.maxWidth = PMWeather.RANDOM.nextInt(15, 25 + (int)(Math.pow((double)((float)this.maxWindspeed / 220.0F), (double)1.75F) * (ServerConfig.maxTornadoWidth - (double)25.0F)));
+ PMWeather.LOGGER.debug("Max Stage: {}, Max Energy: {}, Max Windspeed: {}, Max Width: {}, Touchdown Speed: {}", new Object[]{this.maxStage, this.maxProgress, this.maxWindspeed, this.maxWidth, this.touchdownSpeed});
+ }
+
+ public void aimAtPlayer() {
+ if (this.stormType != 1) {
+ Player nearest = this.level.getNearestPlayer(this.position.x, this.position.y, this.position.z, (double)4096.0F, false);
+ if (nearest != null) {
+ Vec3 aimPos = nearest.position().add(new Vec3((double)(PMWeather.RANDOM.nextFloat() - 0.5F) * ServerConfig.aimAtPlayerOffset, (double)0.0F, (double)(PMWeather.RANDOM.nextFloat() - 0.5F) * ServerConfig.aimAtPlayerOffset));
+ if (this.position.distanceTo(aimPos) >= ServerConfig.aimAtPlayerOffset) {
+ Vec3 toward = this.position.subtract(new Vec3(aimPos.x, this.position.y, aimPos.z)).multiply((double)1.0F, (double)0.0F, (double)1.0F).normalize();
+ double speed = PMWeather.RANDOM.nextDouble() * (double)5.0F + (double)1.0F;
+ this.velocity = toward.multiply(-speed, (double)0.0F, -speed);
+ }
+
+ this.aimedAtPlayer = true;
+ }
+
+ }
+ }
+
+ public void tick() {
+ ++this.tickCount;
+ Iterator vorts = this.vorticies.iterator();
+
+ while(vorts.hasNext()) {
+ Vorticy vorticy = vorts.next();
+ vorticy.tick();
+ if (vorticy.dead) {
+ vorts.remove();
+ }
+ }
+
+ float vorticySpawnChance = 0.05F;
+ if (this.isDying) {
+ vorticySpawnChance = 0.25F;
+ }
+
+ vorticySpawnChance += Mth.clamp((float)Math.pow((double)(((float)this.windspeed - 100.0F) / 200.0F), (double)2.0F), 0.0F, 0.5F);
+ if ((float)this.windspeed >= 39.0F && this.stormType == 2) {
+ vorticySpawnChance *= 2.0F;
+ if (!this.level.isClientSide && PMWeather.RANDOM.nextFloat() < vorticySpawnChance * 0.05F && this.vorticies.size() < 10) {
+ Vorticy vorticy = new Vorticy(this, (float)Math.pow((double)PMWeather.RANDOM.nextFloat(), (double)0.75F) * 0.1F, PMWeather.RANDOM.nextFloat() * 0.15F + 0.05F, 0.075F, PMWeather.RANDOM.nextInt(900, 3000));
+ this.vorticies.add(vorticy);
+ }
+ }
+
+ if (this.stage == 3 && (float)this.windspeed >= 40.0F && this.stormType == 0) {
+ ++this.tornadoOnGroundTicks;
+ if (!this.level.isClientSide && PMWeather.RANDOM.nextFloat() < vorticySpawnChance * 0.05F && this.vorticies.size() < 10) {
+ Vorticy vorticy = new Vorticy(this, (float)Math.pow((double)PMWeather.RANDOM.nextFloat(), (double)0.75F) * 0.4F, PMWeather.RANDOM.nextFloat() * 0.3F + 0.05F, 1.0F / this.rankineFactor * 0.5F, PMWeather.RANDOM.nextInt(35, 120));
+ this.vorticies.add(vorticy);
+ }
+ }
+
+ if (this.isDying) {
+ ++this.ticksSinceDying;
+ }
+
+ BlockPos blockPos = new BlockPos((int)this.position.x, (int)this.position.y, (int)this.position.z);
+ if (!this.level.isClientSide() && this.stage >= 2 && this.stormType == 0) {
+ float y = 0.0F;
+ int count = 0;
+
+ for(int x = -1; x <= 1; ++x) {
+ for(int z = -1; z <= 1; ++z) {
+ float r = Math.max(this.width, 45.0F);
+ Vec3 samplePos = this.position.add((double)((float)x * r * 0.5F), (double)0.0F, (double)((float)z * r * 0.5F));
+ BlockPos sample = this.level.getHeightmapPos(Types.WORLD_SURFACE_WG, new BlockPos((int)samplePos.x, this.level.getMaxBuildHeight(), (int)samplePos.z));
+ y += (float)sample.getY();
+ ++count;
+ }
+ }
+
+ y /= (float)count;
+ blockPos = new BlockPos((int)this.position.x, (int)y, (int)this.position.z);
+ this.position = new Vec3(this.position.x, Mth.lerp((double)0.01F, this.position.y, (double)y), this.position.z);
+ }
+
+ if (this.tickCount % 20 == 0 && !this.level.isClientSide()) {
+ Level iterator = this.level;
+ if (iterator instanceof ServerLevel) {
+ ServerLevel serverLevel = (ServerLevel)iterator;
+ if (this.windspeed > 40 && this.stormType == 0) {
+ ChunkPos cChunkPos = new ChunkPos(blockPos);
+
+ for(int x = -((int)this.width); (float)x <= this.width; x += 16) {
+ for(int z = -((int)this.width); (float)z <= this.width; z += 16) {
+ ChunkPos chunkPos = new ChunkPos(blockPos.offset(x, 0, z));
+ if (!serverLevel.hasChunk(chunkPos.x, chunkPos.z) && !this.forceLoadedChunks.contains(chunkPos) && serverLevel.isInWorldBounds(blockPos)) {
+ this.forceLoadedChunks.add(chunkPos);
+ serverLevel.setChunkForced(chunkPos.x, chunkPos.z, true);
+ }
+ }
+ }
+
+ Iterator iterator = this.forceLoadedChunks.iterator();
+
+ while(iterator.hasNext()) {
+ ChunkPos cpos = iterator.next();
+ double dist = Math.sqrt((double)cpos.distanceSquared(cChunkPos));
+ if (dist > (double)(this.width * 2.0F / 16.0F)) {
+ iterator.remove();
+ serverLevel.setChunkForced(cpos.x, cpos.z, false);
+ }
+ }
+ } else {
+ Iterator iterator = this.forceLoadedChunks.iterator();
+
+ while(iterator.hasNext()) {
+ ChunkPos cpos = iterator.next();
+ iterator.remove();
+ serverLevel.setChunkForced(cpos.x, cpos.z, false);
+ }
+ }
+ }
+ }
+
+ if (this.tickCount % 10 == 0 && !this.level.isClientSide()) {
+ Level targetProgress = this.level;
+ if (targetProgress instanceof ServerLevel) {
+ ServerLevel serverLevel = (ServerLevel)targetProgress;
+ float lightningChance = 0.0F;
+ if (this.stage == 1) {
+ lightningChance = (float)this.energy / 100.0F;
+ } else if (this.stage == 2) {
+ lightningChance = 1.0F + (float)this.energy / 100.0F;
+ } else if (this.stage > 2) {
+ lightningChance = 2.0F;
+ }
+
+ if (this.visualOnly) {
+ lightningChance = 0.0F;
+ }
+
+ lightningChance = Math.min(lightningChance * 0.035F, 0.1F);
+ if (this.stormType == 1) {
+ lightningChance *= 3.0F;
+ }
+
+ if (PMWeather.RANDOM.nextFloat() <= lightningChance * 0.5F) {
+ Vec3 lPos = this.position.add((double)(PMWeather.RANDOM.nextFloat((float)(-ServerConfig.stormSize), (float)ServerConfig.stormSize) / 2.0F), (double)0.0F, (double)(PMWeather.RANDOM.nextFloat((float)(-ServerConfig.stormSize), (float)ServerConfig.stormSize) / 2.0F));
+ if (this.stormType == 1) {
+ Vec2 stormVel = new Vec2((float)this.velocity.x, (float)this.velocity.z);
+ Vec2 right = (new Vec2(stormVel.y, -stormVel.x)).normalized();
+ Vec2 fwd = stormVel.normalized();
+ right = Util.mulVec2(right, PMWeather.RANDOM.nextFloat((float)(-ServerConfig.stormSize), (float)ServerConfig.stormSize) * 5.0F);
+ fwd = Util.mulVec2(fwd, PMWeather.RANDOM.nextFloat((float)(-ServerConfig.stormSize), (float)ServerConfig.stormSize) / 2.0F);
+ lPos = this.position.add(new Vec3((double)right.x, (double)0.0F, (double)right.y)).add(new Vec3((double)fwd.x, (double)0.0F, (double)fwd.y));
+ }
+
+ int height = this.level.getHeightmapPos(Types.MOTION_BLOCKING, new BlockPos((int)lPos.x, (int)lPos.y, (int)lPos.z)).getY();
+ ((WeatherHandlerServer)this.weatherHandler).syncLightningNew(new Vec3(lPos.x, (double)height, lPos.z));
+ }
+ }
+ }
+
+ int gs = this.growthSpeed / 2;
+ if (this.stormType == 0 && this.stage < 3) {
+ gs = (int)((float)gs / 1.5F);
+ }
+
+ if (this.tickCount % gs == 0) {
+ if (this.stormType == 2 && !this.level.isClientSide()) {
+ this.stage = 0;
+ if (this.windspeed >= 15) {
+ this.stage = 1;
+ }
+
+ if (this.windspeed >= 25) {
+ this.stage = 2;
+ }
+
+ if (this.windspeed >= 40) {
+ this.stage = 3;
+ }
+
+ Float sst = ThermodynamicEngine.GetSST(this.weatherHandler, this.position, this.level, (RadarBlockEntity)null, 0);
+ if (sst != null && this.tickCount <= 48000) {
+ if (sst > 32.0F) {
+ sst = 32.0F;
+ }
+
+ float v = 24.0F;
+ if (this.cycloneWindspeed > 60.0F) {
+ v += (this.cycloneWindspeed - 60.0F) / 18.5F;
+ }
+
+ float growth = (sst - v) / 3.5F;
+ if ((float)this.windspeed > 165.0F) {
+ growth -= ((float)this.windspeed - 165.0F) / 15.0F;
+ }
+
+ if (growth < 0.0F) {
+ growth = Math.max(growth, -1.5F);
+ } else {
+ growth *= 1.25F;
+ growth = Math.min(growth, 3.0F);
+ }
+
+ this.cycloneWindspeed += growth;
+ } else {
+ float death = 1.0F;
+ death += Math.max((this.cycloneWindspeed - 75.0F) / 100.0F, 0.0F);
+ this.cycloneWindspeed -= death * 0.25F;
+ }
+
+ this.windspeed = Math.round(this.cycloneWindspeed);
+ if (this.windspeed < -5) {
+ this.dead = true;
+ }
+ } else if (!this.isDying) {
+ int targetProgress = this.maxProgress;
+ if (this.maxStage > this.stage) {
+ targetProgress = 100;
+ }
+
+ if (this.energy < targetProgress) {
+ ++this.energy;
+ if (this.stormType == 1) {
+ this.coldEnergy = Math.clamp((long)(this.coldEnergy + 1), 0, this.maxColdEnergy);
+ }
+ }
+
+ if (this.stage >= 3 && this.stormType == 0) {
+ if (this.windspeed < this.maxWindspeed) {
+ ++this.windspeed;
+ this.occlusion = Math.clamp(this.occlusion - 0.025F, 0.0F, 1.0F);
+ }
+
+ if (this.windspeed >= this.maxWindspeed) {
+ this.isDying = true;
+ this.growthSpeed = PMWeather.RANDOM.nextInt(20, 70);
+ }
+ } else if (this.stage >= this.maxStage && this.energy >= targetProgress) {
+ this.isDying = true;
+ this.growthSpeed = PMWeather.RANDOM.nextInt(40, 80);
+ if (PMWeather.RANDOM.nextInt(2) == 0 || this.maxWidth > 200) {
+ this.maxWidth = Math.min(this.maxWidth, PMWeather.RANDOM.nextInt(5, 35));
+ }
+ }
+
+ if (this.energy >= 100) {
+ this.energy = 0;
+ if (this.stormType == 0) {
+ if (this.stage < 3 && this.stage < this.maxStage) {
+ ++this.stage;
+ if (this.stage == 3) {
+ this.windspeed = 0;
+ }
+ }
+ } else if (this.stage < this.maxStage) {
+ ++this.stage;
+ }
+ }
+ } else if (this.ticksSinceDying > (this.stormType == 1 ? 2400 : 1200)) {
+ if (this.stage >= 3 && this.stormType == 0) {
+ if (this.windspeed < 85 && this.windspeed > 15) {
+ if (PMWeather.RANDOM.nextInt(2) == 0 && !this.level.isClientSide()) {
+ --this.windspeed;
+ }
+ } else {
+ --this.windspeed;
+ }
+
+ this.occlusion = Math.clamp(this.occlusion + 0.015F, 0.0F, 1.0F);
+ if (this.windspeed <= 0) {
+ this.windspeed = 0;
+ --this.stage;
+ this.energy = 100;
+ }
+ } else {
+ --this.energy;
+ if (this.energy <= 0) {
+ this.energy = 100;
+ --this.stage;
+ if (this.stage < 0) {
+ this.energy = 0;
+ this.stage = 0;
+ if (this.coldEnergy > 0) {
+ --this.coldEnergy;
+ } else {
+ this.dead = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (Config.DEBUG) {
+ PMWeather.LOGGER.debug("Stage: {}, Energy: {}, Windspeed: {}, Width: {}", new Object[]{this.stage, this.energy, this.windspeed, this.width});
+ }
+ }
+
+ if (this.stormType == 0) {
+ this.width = Mth.lerp(0.025F, this.width, Math.max(5.0F, Math.clamp((float)this.windspeed / (float)this.maxWindspeed, 0.1F, 1.0F) * (float)this.maxWidth));
+ } else if (this.stormType == 2) {
+ this.width = (float)this.maxWidth;
+ }
+
+ Vec3 vel = this.velocity.multiply((double)0.05F, (double)0.05F, (double)0.05F).multiply((double)2.0F, (double)0.0F, (double)2.0F);
+ if (!this.aimedAtPlayer) {
+ vel = vel.add((new Vec3((double)0.0F, (double)0.0F, (double)-3.0F)).multiply((double)(0.05F * this.occlusion), (double)(0.05F * this.occlusion), (double)(0.05F * this.occlusion)));
+ }
+
+ this.position = this.position.add(vel);
+ if (!this.aimedAtPlayer) {
+ if (this.stormType != 1) {
+ this.velocity = this.velocity.multiply((double)0.985F, (double)0.985F, (double)0.985F);
+ Vec3 baseWind = WindEngine.getWind(new Vec3(this.position.x, (double)(this.level.getMaxBuildHeight() + 1), this.position.z), this.level, true, true, false, false);
+ float factor = 0.018181818F;
+ if (this.stormType == 2) {
+ factor = 0.05F;
+ }
+
+ Vec3 velAdd = (new Vec3(baseWind.x, (double)0.0F, baseWind.z)).multiply((double)factor, (double)0.0F, (double)factor);
+ this.velocity = this.velocity.add(velAdd.multiply((double)0.05F, (double)0.05F, (double)0.05F));
+ }
+
+ if (!this.level.isClientSide() && this.stage >= 3 && ServerConfig.aimAtPlayer && this.stormType == 0) {
+ this.aimAtPlayer();
+ }
+ }
+
+ if (!this.level.isClientSide() && this.tickCount % this.getUpdateRate() == 0) {
+ WeatherHandlerServer weatherHandlerServer = (WeatherHandlerServer)this.weatherHandler;
+ weatherHandlerServer.syncStormUpdate(this);
+ }
+
+ if (this.level.isClientSide()) {
+ this.tickClient();
+ } else if (this.stage >= 3 && this.stormType == 0) {
+ if (this.windspeed >= 40) {
+ AABB aabb = new AABB(this.position.x, this.position.y, this.position.z, this.position.x, this.position.y, this.position.z);
+ aabb = aabb.inflate((double)this.width / (double)2.0F, (double)85.0F, (double)this.width / (double)2.0F);
+
+ for(Entity entity : this.level.getEntities((Entity)null, aabb)) {
+ if (entity instanceof Player) {
+ Player player = (Player)entity;
+ if (!player.isCreative() && !player.isSpectator()) {
+ this.pull(entity, 2.5F);
+ continue;
+ }
+ }
+
+ if (!(entity instanceof Player)) {
+ this.pull(entity, 2.5F);
+ }
+ }
+
+ boolean dd = this.tickCount % 5 == 0 || !ServerConfig.damageEvery5thTick;
+ if (dd) {
+ int windfieldWidth = Math.max((int)this.width, 40);
+ int numBlocks = Math.min(windfieldWidth * Math.max(windfieldWidth / 2, 20) + this.windspeed * 3 + 300, ServerConfig.maxBlocksDamagedPerTick);
+ Map checkedMap = new HashMap();
+ Map chunkMap = new HashMap();
+ int damaged = 0;
+ int damageMax = (500 + (int)this.width) / 3;
+
+ for(int i = 0; i < numBlocks && damaged < damageMax; ++i) {
+ int x = (int)(PMWeather.RANDOM.nextFloat() * (float)windfieldWidth * 2.0F - (float)windfieldWidth);
+ int z = (int)(PMWeather.RANDOM.nextFloat() * (float)windfieldWidth * 2.0F - (float)windfieldWidth);
+ Vec3i off = new Vec3i(x, 0, z);
+ if (!checkedMap.containsKey(off)) {
+ checkedMap.put(off, true);
+ double dist = off.distSqr(Vec3i.ZERO);
+ if (!(dist > (double)(windfieldWidth * windfieldWidth))) {
+ float percAdj = 16.0F;
+ if (ServerConfig.damageEvery5thTick) {
+ percAdj *= 5.0F;
+ }
+
+ BlockPos bPos = blockPos.offset(off.getX(), 60, off.getZ());
+ if (this.level.isInWorldBounds(bPos)) {
+ BlockPos blockPosTop = this.level.getHeightmapPos(Types.MOTION_BLOCKING, bPos).below();
+ double windEffect = (double)this.getWind(blockPosTop.getCenter());
+ if (!(windEffect < (double)40.0F)) {
+ ChunkPos chunkPos = new ChunkPos(SectionPos.blockToSectionCoord(blockPosTop.getX()), SectionPos.blockToSectionCoord(blockPosTop.getZ()));
+ LevelChunk chunk;
+ if (chunkMap.containsKey(chunkPos)) {
+ chunk = chunkMap.get(chunkPos);
+ } else {
+ PMWeather.LOGGER.debug("{}", chunkPos);
+ chunk = this.level.getChunk(chunkPos.x, chunkPos.z);
+ chunkMap.put(chunkPos, chunk);
+ }
+
+ this.doDamage(chunk, blockPosTop, windEffect, percAdj, windfieldWidth);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ public void doDamage(LevelChunk chunk, BlockPos blockPosTop, double windEffect, float percAdj, int windfieldWidth) {
+ BlockState state = chunk.getBlockState(blockPosTop);
+ BlockPos randomDown = blockPosTop.below(PMWeather.RANDOM.nextInt(10));
+ BlockState stateDown = chunk.getBlockState(randomDown);
+ boolean downBlacklisted = false;
+
+ for(TagKey tag : ServerConfig.blacklistedBlockTags) {
+ if (stateDown.is(tag)) {
+ downBlacklisted = true;
+ break;
+ }
+ }
+
+ if (!downBlacklisted && !ServerConfig.blacklistedBlocks.contains(stateDown.getBlock())) {
+ if (stateDown.is(Blocks.GLASS_BLOCKS) || stateDown.is(Blocks.GLASS_PANES)) {
+ double percChance = Math.clamp((windEffect - (double)75.0F) / (double)15.0F, (double)0.0F, (double)1.0F);
+ if ((double)PMWeather.RANDOM.nextFloat() <= percChance * (double)(0.3F * percAdj) && Util.canWindAffect(randomDown.getCenter(), this.level)) {
+ this.level.removeBlock(randomDown, false);
+ this.level.playSound((Player)null, randomDown, SoundEvents.GLASS_BREAK, SoundSource.BLOCKS, 1.0F, PMWeather.RANDOM.nextFloat(0.8F, 1.2F));
+ }
+ }
+
+ if (stateDown.is(BlockTags.LOGS) && !stateDown.is(Blocks.STRIPPED_LOGS) && ServerConfig.doDebarking) {
+ double percChance = Math.clamp((windEffect - (double)140.0F) / (double)20.0F, (double)0.0F, (double)1.0F);
+ if ((double)PMWeather.RANDOM.nextFloat() <= percChance * (double)(0.5F * percAdj) && Util.canWindAffect(randomDown.getCenter(), this.level)) {
+ Block replacement = Util.STRIPPED_VARIANTS.getOrDefault(stateDown.getBlock(), net.minecraft.world.level.block.Blocks.STRIPPED_OAK_LOG);
+ this.level.setBlockAndUpdate(randomDown, (BlockState)replacement.defaultBlockState().trySetValue(BlockStateProperties.AXIS, (Direction.Axis)stateDown.getOptionalValue(BlockStateProperties.AXIS).orElse(Axis.Y)));
+ }
+ }
+ }
+
+ BlockState aboveState = chunk.getBlockState(blockPosTop.above());
+ if (!aboveState.isAir()) {
+ Block aboveBlock = aboveState.getBlock();
+ float blockStrength = getBlockStrength(aboveBlock, this.level, blockPosTop.above());
+ double percChance = Math.clamp(Math.pow(Math.clamp(Math.max(windEffect - (double)blockStrength, (double)0.0F) / (double)20.0F, (double)0.0F, (double)1.0F), (double)4.0F) + 0.02, (double)0.0F, (double)1.0F) * 0.05 * (double)percAdj;
+ if (windEffect < (double)blockStrength) {
+ percChance = (double)0.0F;
+ }
+
+ if (aboveBlock.defaultDestroyTime() < 0.05F && aboveBlock.defaultDestroyTime() >= 0.0F && !ServerConfig.blacklistedBlocks.contains(aboveBlock) && (double)PMWeather.RANDOM.nextFloat() <= percChance) {
+ this.level.removeBlock(blockPosTop.above(), false);
+ return;
+ }
+
+ boolean blacklisted = false;
+
+ for(TagKey tag : ServerConfig.blacklistedBlockTags) {
+ if (aboveBlock.defaultBlockState().is(tag)) {
+ blacklisted = true;
+ break;
+ }
+ }
+
+ if (windEffect >= (double)blockStrength && aboveBlock.defaultDestroyTime() > 0.0F && !ServerConfig.blacklistedBlocks.contains(aboveBlock) && !blacklisted && state.getFluidState().isEmpty() && (double)PMWeather.RANDOM.nextFloat() <= percChance) {
+ this.level.removeBlock(blockPosTop.above(), false);
+ }
+ }
+
+ if (!state.is(net.minecraft.world.level.block.Blocks.GRASS_BLOCK) && !state.is((Block)ModBlocks.SCOURED_GRASS.get())) {
+ if (state.is(net.minecraft.world.level.block.Blocks.DIRT)) {
+ double percChance = Math.clamp((windEffect - (double)170.0F) / (double)40.0F, (double)0.0F, (double)1.0F);
+ if ((double)PMWeather.RANDOM.nextFloat() <= percChance * (double)(0.02F * percAdj)) {
+ this.level.setBlockAndUpdate(blockPosTop, ((Block)ModBlocks.MEDIUM_SCOURING.get()).defaultBlockState());
+ }
+
+ } else if (state.is((Block)ModBlocks.MEDIUM_SCOURING.get())) {
+ double percChance = Math.clamp((windEffect - (double)200.0F) / (double)30.0F, (double)0.0F, (double)1.0F);
+ if ((double)PMWeather.RANDOM.nextFloat() <= percChance * (double)(0.02F * percAdj)) {
+ this.level.setBlockAndUpdate(blockPosTop, ((Block)ModBlocks.HEAVY_SCOURING.get()).defaultBlockState());
+ }
+
+ } else {
+ Block block = state.getBlock();
+ float blockStrength = getBlockStrength(block, this.level, blockPosTop);
+ if (state.is(Blocks.STRIPPED_LOGS)) {
+ blockStrength *= 2.0F;
+ }
+
+ if (ServerConfig.blockStrengths.containsKey(block)) {
+ blockStrength = (Float)ServerConfig.blockStrengths.get(block);
+ }
+
+ double stretch = (double)35.0F;
+ if (state.is(BlockTags.LEAVES)) {
+ stretch = (double)70.0F;
+ } else if (state.is(BlockTags.LOGS) || state.is(BlockTags.PLANKS)) {
+ stretch = (double)50.0F;
+ }
+
+ double percChance = Math.clamp(Math.pow(Math.clamp(Math.max(windEffect - (double)blockStrength, (double)0.0F) / stretch, (double)0.0F, (double)1.0F), (double)4.0F) + 0.02, (double)0.0F, (double)1.0F) * 0.05 * (double)percAdj;
+ if (windEffect < (double)blockStrength) {
+ percChance = (double)0.0F;
+ }
+
+ if (block.defaultDestroyTime() < 0.05F && block.defaultDestroyTime() >= 0.0F && !ServerConfig.blacklistedBlocks.contains(block) && (double)PMWeather.RANDOM.nextFloat() <= percChance) {
+ this.level.removeBlock(blockPosTop, false);
+ } else {
+ boolean blacklisted = false;
+
+ for(TagKey tag : ServerConfig.blacklistedBlockTags) {
+ if (block.defaultBlockState().is(tag)) {
+ blacklisted = true;
+ break;
+ }
+ }
+
+ if (windEffect >= (double)blockStrength && block.defaultDestroyTime() > 0.0F && !ServerConfig.blacklistedBlocks.contains(block) && !blacklisted && state.getFluidState().isEmpty() && (double)PMWeather.RANDOM.nextFloat() <= percChance) {
+ MovingBlock movingBlock = (MovingBlock)((EntityType)ModEntities.MOVING_BLOCK.get()).create(this.level);
+ if (movingBlock != null) {
+ movingBlock.setStartPos(blockPosTop);
+ movingBlock.setBlockState(state);
+ movingBlock.setPos((double)blockPosTop.getX(), (double)blockPosTop.getY(), (double)blockPosTop.getZ());
+ this.level.removeBlock(blockPosTop, false);
+ Player nearest = this.level.getNearestPlayer((double)blockPosTop.getX(), (double)blockPosTop.getY(), (double)blockPosTop.getZ(), (double)128.0F, false);
+ if (PMWeather.RANDOM.nextInt(Math.max(1, windfieldWidth / 10)) == 0 && nearest != null && nearest.position().distanceTo(blockPosTop.getCenter()) < (double)128.0F) {
+ if (this.level.isLoaded(blockPosTop)) {
+ this.level.addFreshEntity(movingBlock);
+ } else {
+ movingBlock.discard();
+ }
+ } else {
+ movingBlock.discard();
+ ((WeatherHandlerServer)this.weatherHandler).syncBlockParticleNew(blockPosTop, state, this);
+ }
+ }
+ }
+
+ }
+ }
+ } else {
+ double percChance = Math.clamp((windEffect - (double)140.0F) / (double)80.0F, (double)0.0F, (double)1.0F);
+ if ((double)PMWeather.RANDOM.nextFloat() <= percChance * (double)(0.02F * percAdj)) {
+ this.level.setBlockAndUpdate(blockPosTop, net.minecraft.world.level.block.Blocks.DIRT.defaultBlockState());
+ }
+
+ }
+ }
+
+ public float getRankine(double dist, int windfieldWidth) {
+ float rankineWidth = (float)windfieldWidth / this.rankineFactor;
+ float perc = 0.0F;
+ if (dist <= (double)(rankineWidth / 2.0F)) {
+ perc = (float)dist / (rankineWidth / 2.0F);
+ } else if (dist <= (double)((float)windfieldWidth * 2.0F)) {
+ perc = Math.clamp((float)Math.pow((double)1.0F - (dist - (double)(rankineWidth / 2.0F)) / (double)(((float)windfieldWidth * 2.0F - rankineWidth) / 2.0F), (double)1.5F), 0.0F, 1.0F);
+ }
+
+ if (Float.isNaN(perc)) {
+ perc = 0.0F;
+ }
+
+ return perc;
+ }
+
+ public float getWind(Vec3 pos) {
+ int windfieldWidth = Math.max((int)this.width, 40);
+ double dist = this.position.multiply((double)1.0F, (double)0.0F, (double)1.0F).distanceTo(pos.multiply((double)1.0F, (double)0.0F, (double)1.0F));
+ float perc = this.getRankine(dist, windfieldWidth);
+ float affectPerc = (float)Math.sqrt((double)1.0F - dist / (double)((float)windfieldWidth * 2.0F));
+ Vec3 relativePos = pos.subtract(this.position);
+ Vec3 rotational = (new Vec3(relativePos.z, (double)0.0F, -relativePos.x)).normalize();
+ Vec3 rPosNoise = this.rotateV3(relativePos, (double)this.tickCount / (double)60.0F);
+ double wNoise = this.FBM(new Vec3(rPosNoise.x / (double)100.0F, rPosNoise.z / (double)100.0F, (double)this.tickCount / (double)200.0F), 5, 2.0F, 0.5F, 1.0F);
+ double realWind = (double)this.windspeed * ((double)1.0F + wNoise * 0.1);
+ Vec3 motion = rotational.multiply(realWind * (double)perc, (double)0.0F, realWind * (double)perc);
+ motion = motion.add(this.velocity.multiply((double)(15.0F * affectPerc), (double)0.0F, (double)(15.0F * affectPerc)));
+
+ for(Vorticy vorticy : this.vorticies) {
+ double d = vorticy.getPosition().multiply((double)1.0F, (double)0.0F, (double)1.0F).distanceTo(pos.multiply((double)1.0F, (double)0.0F, (double)1.0F));
+ Vec3 rPos = pos.subtract(vorticy.getPosition());
+ Vec3 rot = (new Vec3(rPos.z, (double)0.0F, -rPos.x)).normalize();
+ int windWid = (int)((float)windfieldWidth * vorticy.widthPerc);
+ float p = this.getRankine(d, windWid);
+ float wind = vorticy.windspeedMult * (float)this.windspeed;
+ motion = motion.add(rot.multiply((double)(wind * p), (double)0.0F, (double)(wind * p)));
+ }
+
+ return (float)motion.length();
+ }
+
+ public void initFirstTime() {
+ this.ID = (long)(LastUsedStormID++);
+ }
+
+ public void pull(Particle particle, float multiplier) {
+ int windfieldWidth = Math.max((int)this.width, 40);
+ BlockPos blockPos = new BlockPos((int)particle.getPos().x, (int)particle.getPos().y, (int)particle.getPos().z);
+ int worldHeight = this.level.getHeightmapPos(Types.MOTION_BLOCKING, blockPos).getY();
+ if (worldHeight <= blockPos.getY()) {
+ double dist = particle.getPos().distanceTo(new Vec3(this.position.x, particle.getPos().y, this.position.z));
+ if (!(dist > (double)windfieldWidth)) {
+ Vec3 relativePos = particle.getPos().subtract(this.position);
+ double heightDifference = particle.getPos().y - this.position.y;
+ if (!(Math.abs(heightDifference) > (double)150.0F)) {
+ Vec3 inward = (new Vec3(-relativePos.x, (double)0.0F, -relativePos.z)).normalize();
+ Vec3 rotational = (new Vec3(relativePos.z, (double)0.0F, -relativePos.x)).normalize();
+ double windEffect = (double)this.getWind(particle.getPos());
+ double effectStrength = Math.clamp(windEffect / (double)Math.max((float)this.windspeed, 130.0F), (double)0.0F, (double)1.0F) * (double)multiplier;
+ double pullFactor = (double)4.0F;
+ pullFactor -= Math.max(heightDifference, (double)0.0F) / (double)100.0F * (double)3.0F;
+ pullFactor /= (double)Math.max(this.width / 100.0F, 1.0F);
+ if (dist <= (double)(this.width / (this.rankineFactor * 2.0F))) {
+ pullFactor = (double)-1.5F;
+ }
+
+ Vec3 add = inward.multiply(effectStrength * pullFactor, effectStrength * pullFactor, effectStrength * pullFactor).add(rotational.multiply(effectStrength, effectStrength, effectStrength));
+ add = add.add(new Vec3((double)0.0F, effectStrength, (double)0.0F));
+ if (particle instanceof ParticleData) {
+ ParticleData particleData = (ParticleData)particle;
+ particleData.addVelocity(add.multiply((double)0.05F, (double)0.05F, (double)0.05F));
+ }
+
+ }
+ }
+ }
+ }
+
+ public void pull(Entity entity, float multiplier) {
+ int windfieldWidth = Math.max((int)this.width, 40);
+ int worldHeight = this.level.getHeightmapPos(Types.MOTION_BLOCKING, entity.blockPosition()).getY();
+ if (worldHeight <= entity.blockPosition().getY()) {
+ double dist = entity.position().distanceTo(new Vec3(this.position.x, entity.position().y, this.position.z));
+ if (!(dist > (double)windfieldWidth)) {
+ Vec3 relativePos = entity.position().subtract(this.position);
+ double heightDifference = entity.position().y - this.position.y;
+ if (!(Math.abs(heightDifference) > (double)150.0F)) {
+ Vec3 inward = (new Vec3(-relativePos.x, (double)0.0F, -relativePos.z)).normalize();
+ Vec3 rotational = (new Vec3(relativePos.z, (double)0.0F, -relativePos.x)).normalize();
+ double windEffect = (double)this.getWind(entity.position());
+ if (!(windEffect < (double)60.0F)) {
+ double effectStrength = Math.clamp((windEffect - (double)60.0F) / (double)Math.max((float)this.windspeed * 1.2F, 130.0F), (double)0.0F, (double)1.0F) * (double)multiplier * (double)1.5F;
+ double pullFactor = (double)4.0F;
+ pullFactor -= Math.max(heightDifference, (double)0.0F) / (double)65.0F * (double)3.0F;
+ if (dist <= (double)(this.width / this.rankineFactor)) {
+ pullFactor = (double)-1.5F;
+ }
+
+ Vec3 add = inward.multiply(effectStrength * pullFactor, effectStrength * pullFactor, effectStrength * pullFactor).add(rotational.multiply(effectStrength, effectStrength, effectStrength));
+ add = add.add(new Vec3((double)0.0F, effectStrength, (double)0.0F));
+ entity.addDeltaMovement(add.multiply((double)0.05F, (double)0.05F, (double)0.05F));
+ Vec3 motion = entity.getDeltaMovement();
+ if (motion.y > (double)-0.25F) {
+ entity.fallDistance = 0.0F;
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+ @OnlyIn(Dist.CLIENT)
+ public void tickClient() {
+ Player player = Minecraft.getInstance().player;
+ if (player != null && (this.undergroundWind == null || this.undergroundWind.isStopped()) && !this.dead) {
+ this.undergroundWind = new MovingSoundStreamingSource(this, (SoundEvent)ModSounds.UNDERGROUND_WIND.value(), SoundSource.WEATHER, 0.1F, 1.0F, (float)this.maxWidth, true, 3);
+ Minecraft.getInstance().getSoundManager().play(this.undergroundWind);
+ }
+
+ if (player != null && this.stormType == 2) {
+ this.smoothWidth = this.width;
+ this.smoothWindspeed = Mth.lerp(0.1F, this.smoothWindspeed, (float)this.windspeed);
+ if ((this.eyewallWind == null || this.eyewallWind.isStopped()) && !this.dead) {
+ this.eyewallWind = new MovingSoundStreamingSource(this, (SoundEvent)ModSounds.EYEWALL_WIND.value(), SoundSource.WEATHER, 0.1F, 1.0F, (float)this.maxWidth, true, 2);
+ Minecraft.getInstance().getSoundManager().play(this.eyewallWind);
+ }
+ }
+
+ if (player != null && this.stormType == 0) {
+ this.smoothWindspeed = Mth.lerp(0.1F, this.smoothWindspeed, (float)this.windspeed);
+ this.smoothWidth = Mth.lerp(0.05F, this.smoothWidth, this.width);
+ if (this.stage >= 3) {
+ if ((this.tornadicWind == null || this.tornadicWind.isStopped()) && !this.dead) {
+ this.tornadicWind = new MovingSoundStreamingSource(this, (SoundEvent)ModSounds.TORNADIC_WIND.value(), SoundSource.WEATHER, 0.1F, 1.0F, this.width, true, 1);
+ Minecraft.getInstance().getSoundManager().play(this.tornadicWind);
+ }
+
+ if ((this.tornadicDamage == null || this.tornadicDamage.isStopped()) && !this.dead) {
+ this.tornadicDamage = new MovingSoundStreamingSource(this, (SoundEvent)ModSounds.TORNADIC_DAMAGE.value(), SoundSource.WEATHER, 0.1F, 1.0F, this.width, true, 4);
+ Minecraft.getInstance().getSoundManager().play(this.tornadicDamage);
+ }
+
+ if (this.windspeed >= 40 && !player.isCreative() && !player.isSpectator()) {
+ this.pull((Entity)player, 2.5F);
+ }
+ }
+
+ if (this.stage >= 2 && (this.supercellWind == null || this.supercellWind.isStopped()) && !this.dead) {
+ this.supercellWind = new MovingSoundStreamingSource(this, (SoundEvent)ModSounds.SUPERCELL_WIND.value(), SoundSource.WEATHER, 0.1F, 1.0F, this.width, true, 0);
+ Minecraft.getInstance().getSoundManager().play(this.supercellWind);
+ }
+
+ if (this.stage < 3 && this.tornadicWind != null) {
+ this.tornadicWind.stopPlaying();
+ this.tornadicWind = null;
+ }
+
+ if (this.stage < 2 && this.supercellWind != null) {
+ this.supercellWind.stopPlaying();
+ this.supercellWind = null;
+ }
+
+ for(int i = 0; i < this.listParticleDebris.size(); ++i) {
+ EntityRotFX debris = this.listParticleDebris.get(i);
+ if (!debris.isAlive()) {
+ this.listParticleDebris.remove(debris);
+ } else {
+ this.pull((Particle)debris, 1.0F);
+ }
+ }
+ }
+
+ }
+
+ public void remove() {
+ this.dead = true;
+ if (EffectiveSide.get().equals(LogicalSide.CLIENT)) {
+ this.cleanupClient();
+ }
+
+ this.cleanup();
+ }
+
+ public void cleanup() {
+ this.weatherHandler = null;
+ if (!this.level.isClientSide()) {
+ for(ChunkPos chunkPos : this.forceLoadedChunks) {
+ ((ServerLevel)this.level).setChunkForced(chunkPos.x, chunkPos.z, false);
+ }
+ }
+
+ }
+
+ @OnlyIn(Dist.CLIENT)
+ public void cleanupClient() {
+ if (this.tornadicWind != null) {
+ this.tornadicWind.stopPlaying();
+ this.tornadicWind = null;
+ }
+
+ if (this.tornadicDamage != null) {
+ this.tornadicDamage.stopPlaying();
+ this.tornadicDamage = null;
+ }
+
+ if (this.supercellWind != null) {
+ this.supercellWind.stopPlaying();
+ this.supercellWind = null;
+ }
+
+ if (this.eyewallWind != null) {
+ this.eyewallWind.stopPlaying();
+ this.eyewallWind = null;
+ }
+
+ if (this.undergroundWind != null) {
+ this.undergroundWind.stopPlaying();
+ this.undergroundWind = null;
+ }
+
+ }
+
+ public void read() {
+ this.nbtSyncFromServer();
+ }
+
+ public void write() {
+ this.nbtSyncForClient();
+ }
+
+ public int getUpdateRate() {
+ return this.stormType == 0 && this.stage >= 3 ? 2 : 40;
+ }
+
+ public void nbtSyncFromServer() {
+ CachedNBTTagCompound nbt = this.getNBTCache();
+ this.ID = nbt.getLong("ID");
+ this.onWater = nbt.getBoolean("onWater");
+ this.position = new Vec3(nbt.getDouble("positionX"), nbt.getDouble("positionY"), nbt.getDouble("positionZ"));
+ this.velocity = new Vec3(nbt.getDouble("velocityX"), nbt.getDouble("velocityY"), nbt.getDouble("velocityZ"));
+ this.windspeed = nbt.getInt("windspeed");
+ this.cycloneWindspeed = (float)this.windspeed;
+ this.width = nbt.getFloat("width");
+ this.energy = nbt.getInt("energy");
+ this.coldEnergy = nbt.getInt("coldEnergy");
+ this.stormType = nbt.getInt("stormType");
+ this.stage = nbt.getInt("stage");
+ this.dead = nbt.getBoolean("dead");
+ this.isDying = nbt.getBoolean("isDying");
+ this.maxWidth = nbt.getInt("maxWidth");
+ this.maxWindspeed = nbt.getInt("maxWindspeed");
+ this.maxStage = nbt.getInt("maxStage");
+ this.maxProgress = nbt.getInt("maxProgress");
+ this.ticksSinceDying = nbt.getInt("ticksSinceDying");
+ this.growthSpeed = nbt.getInt("growthSpeed");
+ this.visualOnly = nbt.getBoolean("visualOnly");
+ this.aimedAtPlayer = nbt.getBoolean("aimedAtPlayer");
+ this.cirus = nbt.getBoolean("cirus");
+ this.touchdownSpeed = nbt.getInt("touchdownSpeed");
+ this.occlusion = nbt.getFloat("occlusion");
+ CompoundTag vorticiesData = nbt.get("vorticies");
+ int vorticyCount = vorticiesData.getInt("vorticyCount");
+ this.vorticies.clear();
+
+ for(int i = 0; i < vorticyCount; ++i) {
+ CompoundTag vorticyData = vorticiesData.getCompound("vorticy" + i);
+ Vorticy vorticy = new Vorticy(this, vorticyData.getFloat("maxWindspeedMult"), vorticyData.getFloat("widthPerc"), vorticyData.getFloat("distancePerc"), vorticyData.getInt("lifetime"));
+ vorticy.dead = vorticyData.getBoolean("dead");
+ vorticy.angle = vorticyData.getFloat("angle");
+ vorticy.tickCount = vorticyData.getInt("tickCount");
+ vorticy.windspeedMult = vorticyData.getFloat("windspeedMult");
+ this.vorticies.add(vorticy);
+ }
+
+ }
+
+ public void nbtSyncForClient() {
+ CachedNBTTagCompound nbt = this.getNBTCache();
+ CompoundTag vorticiesData = new CompoundTag();
+ vorticiesData.putInt("vorticyCount", this.vorticies.size());
+
+ for(int i = 0; i < this.vorticies.size(); ++i) {
+ Vorticy vorticy = this.vorticies.get(i);
+ CompoundTag vorticyData = new CompoundTag();
+ vorticyData.putBoolean("dead", vorticy.dead);
+ vorticyData.putFloat("windspeedMult", vorticy.windspeedMult);
+ vorticyData.putFloat("maxWindspeedMult", vorticy.maxWindspeedMult);
+ vorticyData.putFloat("widthPerc", vorticy.widthPerc);
+ vorticyData.putFloat("distancePerc", vorticy.distancePerc);
+ vorticyData.putFloat("angle", vorticy.angle);
+ vorticyData.putInt("lifetime", vorticy.lifetime);
+ vorticyData.putInt("tickCount", vorticy.tickCount);
+ vorticiesData.put("vorticy" + i, vorticyData);
+ }
+
+ nbt.put("vorticies", vorticiesData);
+ nbt.putBoolean("onWater", this.onWater);
+ nbt.putInt("touchdownSpeed", this.touchdownSpeed);
+ nbt.putBoolean("cirus", this.cirus);
+ nbt.putBoolean("aimedAtPlayer", this.aimedAtPlayer);
+ nbt.putBoolean("visualOnly", this.visualOnly);
+ nbt.putBoolean("isDying", this.isDying);
+ nbt.putInt("maxWidth", this.maxWidth);
+ nbt.putInt("maxWindspeed", this.maxWindspeed);
+ nbt.putInt("maxStage", this.maxStage);
+ nbt.putInt("maxProgress", this.maxProgress);
+ nbt.putInt("ticksSinceDying", this.ticksSinceDying);
+ nbt.putInt("growthSpeed", this.growthSpeed);
+ nbt.putFloat("occlusion", this.occlusion);
+ nbt.putDouble("positionX", this.position.x);
+ nbt.putDouble("positionY", this.position.y);
+ nbt.putDouble("positionZ", this.position.z);
+ nbt.putDouble("velocityX", this.velocity.x);
+ nbt.putDouble("velocityY", this.velocity.y);
+ nbt.putDouble("velocityZ", this.velocity.z);
+ nbt.putLong("ID", this.ID);
+ nbt.getNewNBT().putLong("ID", this.ID);
+ nbt.putInt("windspeed", this.windspeed);
+ nbt.putFloat("width", this.width);
+ nbt.putInt("energy", this.energy);
+ nbt.putInt("coldEnergy", this.coldEnergy);
+ nbt.putInt("stormType", this.stormType);
+ nbt.putInt("stage", this.stage);
+ nbt.putBoolean("dead", this.dead);
+ }
+
+ public CachedNBTTagCompound getNBTCache() {
+ return this.nbtCache;
+ }
+
+ public static float getBlockStrength(Block block, Level level, @Nullable BlockPos blockPos) {
+ ItemStack item = new ItemStack(Items.IRON_AXE);
+ float destroySpeed = block.defaultBlockState().getDestroySpeed(level, blockPos != null ? blockPos : BlockPos.ZERO);
+
+ try {
+ destroySpeed /= item.getDestroySpeed(block.defaultBlockState());
+ } catch (Exception e) {
+ PMWeather.LOGGER.warn(e.getMessage());
+ }
+
+ return 60.0F + Mth.sqrt(destroySpeed) * 60.0F;
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/Vorticy.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/Vorticy.java
new file mode 100644
index 00000000..19c4744f
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/Vorticy.java
@@ -0,0 +1,68 @@
+package dev.protomanly.pmweather.weather;
+
+import dev.protomanly.pmweather.PMWeather;
+import net.minecraft.util.Mth;
+import net.minecraft.world.phys.Vec3;
+
+public class Vorticy {
+ public float windspeedMult = 0.0F;
+ public float maxWindspeedMult;
+ public float widthPerc;
+ public float distancePerc;
+ public float angle;
+ public int lifetime;
+ public int tickCount;
+ public boolean dead = false;
+ private Storm storm;
+
+ public Vorticy(Storm storm, float maxWindspeedMult, float widthPerc, float distancePerc, int lifetime) {
+ super();
+ this.storm = storm;
+ this.maxWindspeedMult = maxWindspeedMult;
+ this.distancePerc = distancePerc;
+ this.widthPerc = widthPerc;
+ this.lifetime = lifetime;
+ this.angle = PMWeather.RANDOM.nextFloat() * ((float)Math.PI * 2F);
+ }
+
+ public void tick() {
+ if (!this.dead) {
+ ++this.tickCount;
+ float lifeDelta = (float)this.tickCount / (float)this.lifetime;
+ float wind = (float)this.storm.windspeed * (1.0F - this.distancePerc);
+ float angleAdd = (float)Math.toRadians((double)(wind / 300.0F));
+ if ((double)lifeDelta > (double)0.5F) {
+ this.windspeedMult = Mth.lerp((lifeDelta - 0.5F) * 2.0F, this.maxWindspeedMult, 0.0F);
+ } else {
+ this.windspeedMult = Mth.lerp(lifeDelta * 2.0F, 0.0F, this.maxWindspeedMult);
+ }
+
+ if (this.tickCount > this.lifetime) {
+ this.dead = true;
+ }
+
+ if (this.storm.stormType == 2) {
+ angleAdd *= 0.1F;
+ }
+
+ this.angle += angleAdd;
+ if (this.angle > ((float)Math.PI * 2F)) {
+ this.angle = 0.0F;
+ }
+
+ }
+ }
+
+ public float getWidth() {
+ return this.storm.stormType == 2 ? this.widthPerc * (float)this.storm.maxWidth : this.widthPerc * this.storm.width;
+ }
+
+ public float getDistance() {
+ return this.storm.stormType == 2 ? this.distancePerc * (float)this.storm.maxWidth : this.distancePerc * this.storm.width;
+ }
+
+ public Vec3 getPosition() {
+ float realDist = this.getDistance();
+ return this.storm.position.add(new Vec3(Math.sin((double)this.angle) * (double)realDist, (double)0.0F, Math.cos((double)this.angle) * (double)realDist));
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/WeatherHandler.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/WeatherHandler.java
new file mode 100644
index 00000000..d3eeb592
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/WeatherHandler.java
@@ -0,0 +1,268 @@
+package dev.protomanly.pmweather.weather;
+
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.config.ServerConfig;
+import dev.protomanly.pmweather.data.LevelSavedData;
+import dev.protomanly.pmweather.interfaces.IWorldData;
+import dev.protomanly.pmweather.util.Util;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.Vec2;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Vector2f;
+
+public abstract class WeatherHandler implements IWorldData {
+ private List storms = new ArrayList();
+ private ResourceKey dimension;
+ public HashMap lookupStormByID = new HashMap();
+ public long seed;
+
+ public WeatherHandler(ResourceKey dimension) {
+ super();
+ this.dimension = dimension;
+ }
+
+ public void tick() {
+ Level level = this.getWorld();
+ if (level != null) {
+ List stormList = this.getStorms();
+
+ for(int i = 0; i < stormList.size(); ++i) {
+ Storm storm = stormList.get(i);
+ if (this instanceof WeatherHandlerServer) {
+ WeatherHandlerServer weatherHandlerServer = (WeatherHandlerServer)this;
+ if (storm.dead) {
+ this.removeStorm(storm.ID);
+ weatherHandlerServer.syncStormRemove(storm);
+ continue;
+ }
+ }
+
+ if (!storm.dead) {
+ storm.tick();
+ } else {
+ this.removeStorm(storm.ID);
+ }
+ }
+ }
+
+ }
+
+ public List getStorms() {
+ return this.storms;
+ }
+
+ public void addStorm(Storm storm) {
+ if (!this.lookupStormByID.containsKey(storm.ID)) {
+ this.storms.add(storm);
+ this.lookupStormByID.put(storm.ID, storm);
+ } else {
+ PMWeather.LOGGER.warn("Tried to add a storm with existing ID: {}", storm.ID);
+ }
+
+ }
+
+ public void removeStorm(long id) {
+ Storm storm = this.lookupStormByID.get(id);
+ if (storm != null) {
+ storm.remove();
+ this.storms.remove(storm);
+ this.lookupStormByID.remove(id);
+ } else {
+ PMWeather.LOGGER.warn("Tried to remove a non-existent storm with ID: {}", id);
+ }
+
+ }
+
+ public float getPrecipitation(Vec3 pos) {
+ float precip = 0.0F;
+ float cloudDensity = Clouds.getCloudDensity(this, new Vector2f((float)pos.x, (float)pos.z), 0.0F);
+ if (cloudDensity > 0.15F) {
+ precip += (cloudDensity - 0.15F) * 2.0F;
+ }
+
+ for(Storm storm : this.getStorms()) {
+ if (!storm.visualOnly) {
+ double dist = pos.distanceTo(new Vec3(storm.position.x, pos.y, storm.position.z));
+ double perc = (double)0.0F;
+ float smoothStage = (float)storm.stage + (float)storm.energy / 100.0F;
+ if (storm.stage == 3) {
+ smoothStage = 3.0F;
+ }
+
+ if (storm.stormType == 2) {
+ Vec3 cPos = storm.position.multiply((double)1.0F, (double)0.0F, (double)1.0F);
+ float intensity = (float)Math.pow((double)Math.clamp((float)storm.windspeed / 65.0F, 0.0F, 1.0F), (double)0.85F);
+ Vec3 relPos = cPos.subtract(pos);
+ double d = (double)((float)storm.maxWidth / (3.0F + (float)storm.windspeed / 12.0F));
+ double d2 = (double)((float)storm.maxWidth / (1.15F + (float)storm.windspeed / 12.0F));
+ double dE = (double)((float)storm.maxWidth * 0.65F / (1.75F + (float)storm.windspeed / 12.0F));
+ double fac = (double)1.0F + Math.max((dist - (double)((float)storm.maxWidth * 0.2F)) / (double)storm.maxWidth, (double)0.0F) * (double)2.0F;
+ d *= fac;
+ d2 *= fac;
+ double angle = Math.atan2(relPos.z, relPos.x) - dist / d;
+ double angle2 = Math.atan2(relPos.z, relPos.x) - dist / d2;
+ double angleE = Math.atan2(relPos.z, relPos.x) - dist / dE;
+ float weak = 0.0F;
+ float strong = 0.0F;
+ float intense = 0.0F;
+ float staticBands = (float)Math.sin(angle - (Math.PI / 2D));
+ staticBands *= (float)Math.pow(Math.clamp(dist / (double)((float)storm.maxWidth * 0.25F), (double)0.0F, (double)1.0F), (double)0.1F);
+ staticBands *= 1.25F * (float)Math.pow((double)intensity, (double)0.75F);
+ if (staticBands < 0.0F) {
+ weak += Math.abs(staticBands);
+ } else {
+ weak += Math.abs(staticBands) * (float)Math.pow((double)1.0F - Math.clamp(dist / (double)((float)storm.maxWidth * 0.65F), (double)0.0F, (double)1.0F), (double)0.5F);
+ weak *= Math.clamp(((float)storm.windspeed - 70.0F) / 40.0F, 0.0F, 1.0F);
+ }
+
+ float rotatingBands = (float)Math.sin((angle2 + Math.toRadians((double)((float)storm.tickCount / 8.0F))) * (double)6.0F);
+ rotatingBands *= (float)Math.pow(Math.clamp(dist / (double)((float)storm.maxWidth * 0.25F), (double)0.0F, (double)1.0F), (double)0.1F);
+ rotatingBands *= 1.25F * (float)Math.pow((double)intensity, (double)0.75F);
+ strong += Mth.lerp(0.45F, Math.abs(rotatingBands) * 0.3F + 0.7F, weak);
+ intense += Mth.lerp(0.3F, Math.abs(rotatingBands) * 0.2F + 0.8F, weak);
+ weak = (Math.abs(rotatingBands) * 0.3F + 0.6F) * weak;
+ float localRain = 0.0F;
+ localRain += Mth.lerp(Math.clamp(((float)storm.windspeed - 120.0F) / 60.0F, 0.0F, 1.0F), Mth.lerp(Math.clamp(((float)storm.windspeed - 40.0F) / 90.0F, 0.0F, 1.0F), weak, strong), intense);
+ float eye = (float)Math.sin((angleE + Math.toRadians((double)((float)storm.tickCount / 4.0F))) * (double)2.0F);
+ float efc = Mth.lerp(Math.clamp(((float)storm.windspeed - 100.0F) / 50.0F, 0.0F, 1.0F), 0.15F, 0.4F);
+ localRain = Math.max((float)Math.pow((double)1.0F - Math.clamp(dist / (double)((float)storm.maxWidth * efc), (double)0.0F, (double)1.0F), (double)0.5F) * (Math.abs(eye * 0.1F) + 0.9F) * 1.35F * intensity, localRain);
+ localRain *= (float)Math.pow((double)1.0F - Math.clamp(dist / (double)storm.maxWidth, (double)0.0F, (double)1.0F), (double)0.5F);
+ localRain *= Mth.lerp(0.5F + Math.clamp(((float)storm.windspeed - 65.0F) / 40.0F, 0.0F, 1.0F) * 0.5F, 1.0F, (float)Math.pow(Math.clamp(dist / (double)((float)storm.maxWidth * 0.1F), (double)0.0F, (double)1.0F), (double)2.0F));
+ if (localRain > 0.6F) {
+ float dif = (localRain - 0.6F) / 2.5F;
+ localRain -= dif;
+ }
+
+ precip += Math.max(localRain - 0.15F, 0.0F) * 2.0F;
+ }
+
+ if (storm.stormType == 1) {
+ Vec2 v2fWorldPos = new Vec2((float)pos.x, (float)pos.z);
+ Vec2 stormVel = new Vec2((float)storm.velocity.x, (float)storm.velocity.z);
+ Vec2 v2fStormPos = new Vec2((float)storm.position.x, (float)storm.position.z);
+ Vec2 right = (new Vec2(stormVel.y, -stormVel.x)).normalized();
+ Vec2 fwd = stormVel.normalized();
+ Vec2 le = Util.mulVec2(right, -((float)ServerConfig.stormSize) * 5.0F);
+ Vec2 ri = Util.mulVec2(right, (float)ServerConfig.stormSize * 5.0F);
+ Vec2 off = Util.mulVec2(fwd, -((float)Math.pow(Mth.clamp(dist / (double)((float)ServerConfig.stormSize * 5.0F), (double)0.0F, (double)1.0F), (double)2.0F)) * (float)ServerConfig.stormSize * 1.5F);
+ le = le.add(off);
+ ri = ri.add(off);
+ le = le.add(v2fStormPos);
+ ri = ri.add(v2fStormPos);
+ float d = Util.minimumDistance(le, ri, v2fWorldPos);
+ if ((double)d > ServerConfig.stormSize * (double)16.0F) {
+ continue;
+ }
+
+ Vec2 nearPoint = Util.nearestPoint(le, ri, v2fWorldPos);
+ Vec2 facing = v2fWorldPos.add(nearPoint.negated());
+ float behind = -facing.dot(fwd);
+ float sze = (float)ServerConfig.stormSize * 1.5F;
+ sze *= Mth.lerp(Mth.clamp(smoothStage - 1.0F, 0.0F, 1.0F), 4.0F, 12.0F);
+ behind += (float)ServerConfig.stormSize / 2.0F;
+ if (behind > 0.0F) {
+ float p = Mth.clamp(Math.abs(behind) / sze, 0.0F, 1.0F);
+ float start = 0.06F;
+ if (p <= start) {
+ p /= start;
+ } else {
+ p = 1.0F - (p - start) / (1.0F - start);
+ }
+
+ perc = (double)((float)Math.pow((double)Mth.clamp(p, 0.0F, 1.0F), (double)3.0F));
+ }
+
+ if (storm.stage <= 0) {
+ perc = (double)0.0F;
+ } else if (storm.stage == 1) {
+ perc *= (double)((float)storm.energy / 100.0F);
+ }
+
+ perc *= (double)Mth.sqrt(1.0F - Mth.clamp(d / sze, 0.0F, 1.0F));
+ }
+
+ if (storm.stormType == 0) {
+ double coreDist = pos.distanceTo(new Vec3(storm.position.x + (double)2000.0F, pos.y, storm.position.z - (double)900.0F));
+ if (Math.min(dist, coreDist) > ServerConfig.stormSize * (double)6.0F) {
+ continue;
+ }
+
+ perc = (double)1.0F - Math.clamp(dist / ServerConfig.stormSize, (double)0.0F, (double)1.0F);
+ if (storm.stage == 0) {
+ perc *= (double)((float)storm.energy / 100.0F);
+ }
+
+ if (storm.stage >= 2) {
+ perc *= (double)Mth.lerp(Math.clamp(smoothStage - 2.0F, 0.0F, 1.0F), 1.0F, storm.occlusion * 0.5F + 0.5F);
+ }
+
+ double p = (double)1.0F - Math.clamp(coreDist / (ServerConfig.stormSize * (double)6.0F), (double)0.0F, (double)1.0F);
+ if (storm.stage <= 1) {
+ p *= (double)0.0F;
+ }
+
+ if (storm.stage >= 2) {
+ p *= (double)Math.clamp((smoothStage - 2.0F) / 0.5F, 0.0F, 1.0F);
+ }
+
+ perc = Math.max(p, perc);
+ }
+
+ precip += (float)perc;
+ }
+ }
+
+ return Math.clamp(precip * (float)ServerConfig.rainStrength, 0.0F, 1.0F);
+ }
+
+ public abstract Level getWorld();
+
+ public CompoundTag save(CompoundTag data) {
+ PMWeather.LOGGER.debug("WeatherHandler save");
+ CompoundTag listStormsNBT = new CompoundTag();
+
+ for(int i = 0; i < this.storms.size(); ++i) {
+ Storm storm = this.storms.get(i);
+ storm.getNBTCache().setUpdateForced(true);
+ storm.write();
+ storm.getNBTCache().setUpdateForced(false);
+ listStormsNBT.put("storm_" + storm.ID, storm.getNBTCache().getNewNBT());
+ }
+
+ data.put("stormData", listStormsNBT);
+ data.putLong("lastUsedIDStorm", Storm.LastUsedStormID);
+ return null;
+ }
+
+ public void read() {
+ LevelSavedData savedData = (LevelSavedData)((ServerLevel)this.getWorld()).getDataStorage().computeIfAbsent(LevelSavedData.factory(), "pmweather_weather_data");
+ savedData.setDataHandler(this);
+ PMWeather.LOGGER.debug("Weather Data: {}", savedData.getData());
+ CompoundTag data = savedData.getData();
+ Storm.LastUsedStormID = data.getLong("lastUsedIDStorm");
+ CompoundTag storms = data.getCompound("stormData");
+
+ for(String tagName : storms.getAllKeys()) {
+ CompoundTag stormData = storms.getCompound(tagName);
+ Storm storm = new Storm(this, this.getWorld(), (Float)null, stormData.getInt("stormType"));
+
+ try {
+ storm.getNBTCache().setNewNBT(stormData);
+ storm.read();
+ storm.getNBTCache().updateCacheFromNew();
+ } catch (Exception e) {
+ PMWeather.LOGGER.error(e.getMessage(), e);
+ }
+
+ this.addStorm(storm);
+ }
+
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/WeatherHandlerClient.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/WeatherHandlerClient.java
new file mode 100644
index 00000000..79bd53b8
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/WeatherHandlerClient.java
@@ -0,0 +1,158 @@
+package dev.protomanly.pmweather.weather;
+
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.config.ClientConfig;
+import dev.protomanly.pmweather.config.ServerConfig;
+import dev.protomanly.pmweather.event.GameBusClientEvents;
+import dev.protomanly.pmweather.particle.ParticleCube;
+import dev.protomanly.pmweather.sound.ModSounds;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtUtils;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.sounds.SoundEvent;
+import net.minecraft.sounds.SoundSource;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.Vec3;
+
+public class WeatherHandlerClient extends WeatherHandler {
+ public List lightnings = new ArrayList();
+
+ public WeatherHandlerClient(ResourceKey dimension) {
+ super(dimension);
+ }
+
+ public Level getWorld() {
+ return Minecraft.getInstance().level;
+ }
+
+ public float getHail() {
+ Player player = Minecraft.getInstance().player;
+ if (player == null) {
+ return 0.0F;
+ } else {
+ float precip = 0.0F;
+
+ for(Storm storm : this.getStorms()) {
+ if (!storm.visualOnly) {
+ double dist = player.position().distanceTo(new Vec3(storm.position.x + (double)2000.0F, player.position().y, storm.position.z - (double)900.0F));
+ if (!(dist > ServerConfig.stormSize * (double)4.0F)) {
+ double perc = (double)0.0F;
+ if (storm.stormType == 0) {
+ perc = (double)1.0F - Math.clamp(dist / (ServerConfig.stormSize * (double)6.0F), (double)0.0F, (double)1.0F);
+ if (storm.stage == 2) {
+ perc *= (double)((float)storm.energy / 100.0F);
+ }
+
+ if (storm.stage > 2) {
+ perc *= (double)1.0F;
+ }
+
+ if (storm.stage < 2) {
+ perc *= (double)0.0F;
+ }
+ }
+
+ precip += (float)perc;
+ }
+ }
+ }
+
+ return Math.clamp(precip, 0.0F, 1.0F);
+ }
+ }
+
+ public float getPrecipitation() {
+ Player player = Minecraft.getInstance().player;
+ return player == null ? 0.0F : this.getPrecipitation(player.position());
+ }
+
+ public void strike(Vec3 pos) {
+ Lightning lightning = new Lightning(pos, this.getWorld());
+ this.lightnings.add(lightning);
+ Player player = Minecraft.getInstance().player;
+ if (player != null) {
+ double dist = player.position().multiply((double)1.0F, (double)0.0F, (double)1.0F).distanceTo(pos.multiply((double)1.0F, (double)0.0F, (double)1.0F));
+ if (dist > (double)256.0F) {
+ this.getWorld().playLocalSound(pos.x, pos.y, pos.z, (SoundEvent)ModSounds.THUNDER_FAR.value(), SoundSource.WEATHER, 5000.0F, PMWeather.RANDOM.nextFloat(0.8F, 1.0F), true);
+ } else {
+ this.getWorld().playLocalSound(pos.x, pos.y, pos.z, (SoundEvent)ModSounds.THUNDER_NEAR.value(), SoundSource.WEATHER, 5000.0F, PMWeather.RANDOM.nextFloat(0.8F, 1.0F), true);
+ }
+ }
+
+ }
+
+ public void tick() {
+ super.tick();
+ Iterator iterator = this.lightnings.iterator();
+
+ while(iterator.hasNext()) {
+ Lightning lightning = iterator.next();
+ if (!lightning.dead && lightning.level == this.getWorld()) {
+ lightning.tick();
+ } else {
+ iterator.remove();
+ }
+ }
+
+ }
+
+ public void nbtSyncFromServer(CompoundTag compoundTag) {
+ String command = compoundTag.getString("command");
+ if (command.equals("syncStormNew")) {
+ CompoundTag stormCompoundTag = compoundTag.getCompound("data");
+ long ID = stormCompoundTag.getLong("ID");
+ PMWeather.LOGGER.debug("syncStormNew, ID: {}", ID);
+ Storm storm = new Storm(this, this.getWorld(), (Float)null, stormCompoundTag.getInt("stormType"));
+ storm.getNBTCache().setNewNBT(stormCompoundTag);
+ storm.nbtSyncFromServer();
+ storm.getNBTCache().updateCacheFromNew();
+ this.addStorm(storm);
+ } else if (command.equals("syncStormRemove")) {
+ CompoundTag stormCompoundTag = compoundTag.getCompound("data");
+ long ID = stormCompoundTag.getLong("ID");
+ Storm storm = this.lookupStormByID.get(ID);
+ if (storm != null) {
+ this.removeStorm(ID);
+ }
+ } else if (command.equals("syncStormUpdate")) {
+ CompoundTag stormCompoundTag = compoundTag.getCompound("data");
+ long ID = stormCompoundTag.getLong("ID");
+ Storm storm = this.lookupStormByID.get(ID);
+ if (storm != null) {
+ storm.getNBTCache().setNewNBT(stormCompoundTag);
+ storm.nbtSyncFromServer();
+ storm.getNBTCache().updateCacheFromNew();
+ }
+ } else if (command.equals("syncBlockParticleNew")) {
+ if ((double)PMWeather.RANDOM.nextFloat() > ClientConfig.debrisParticleDensity) {
+ return;
+ }
+
+ CompoundTag nbt = compoundTag.getCompound("data");
+ Vec3 pos = new Vec3((double)nbt.getInt("positionX"), (double)(nbt.getInt("positionY") + 1), (double)nbt.getInt("positionZ"));
+ BlockState state = NbtUtils.readBlockState(this.getWorld().holderLookup(Registries.BLOCK), nbt.getCompound("blockstate"));
+ long stormID = nbt.getLong("stormID");
+ Storm storm = this.lookupStormByID.get(stormID);
+ if (storm != null) {
+ ParticleCube debris = new ParticleCube((ClientLevel)this.getWorld(), pos.x + (double)((PMWeather.RANDOM.nextFloat() - PMWeather.RANDOM.nextFloat()) * 3.0F), pos.y + (double)((PMWeather.RANDOM.nextFloat() - PMWeather.RANDOM.nextFloat()) * 3.0F), pos.z + (double)((PMWeather.RANDOM.nextFloat() - PMWeather.RANDOM.nextFloat()) * 3.0F), (double)0.0F, (double)0.0F, (double)0.0F, state);
+ GameBusClientEvents.particleBehavior.initParticleCube(debris);
+ storm.listParticleDebris.add(debris);
+ debris.ignoreWind = true;
+ debris.renderRange = 256.0F;
+ debris.spawnAsDebrisEffect();
+ }
+ } else if (command.equals("syncLightningNew")) {
+ CompoundTag nbt = compoundTag.getCompound("data");
+ this.strike(new Vec3(nbt.getDouble("positionX"), nbt.getDouble("positionY"), nbt.getDouble("positionZ")));
+ }
+
+ }
+}
diff --git a/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/WindEngine.java b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/WindEngine.java
new file mode 100644
index 00000000..249eba06
--- /dev/null
+++ b/pmweather_tornado_extraction_export/dev/protomanly/pmweather/weather/WindEngine.java
@@ -0,0 +1,297 @@
+package dev.protomanly.pmweather.weather;
+
+import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.config.ServerConfig;
+import dev.protomanly.pmweather.event.GameBusClientEvents;
+import dev.protomanly.pmweather.event.GameBusEvents;
+import dev.protomanly.pmweather.util.Util;
+import java.util.ArrayList;
+import java.util.List;
+import net.minecraft.core.BlockPos;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.levelgen.LegacyRandomSource;
+import net.minecraft.world.level.levelgen.Heightmap.Types;
+import net.minecraft.world.level.levelgen.synth.SimplexNoise;
+import net.minecraft.world.phys.Vec2;
+import net.minecraft.world.phys.Vec3;
+
+public class WindEngine {
+ public static SimplexNoise simplexNoise;
+
+ public WindEngine() {
+ super();
+ }
+
+ public static void init(WeatherHandler weatherHandler) {
+ simplexNoise = new SimplexNoise(new LegacyRandomSource(weatherHandler.seed));
+ }
+
+ public static double FBM(Vec3 pos, int octaves, float lacunarity, float gain, float amplitude) {
+ double y = (double)0.0F;
+ if (simplexNoise != null) {
+ for(int i = 0; i < Math.max(octaves, 1); ++i) {
+ y += (double)amplitude * simplexNoise.getValue(pos.x, pos.y, pos.z);
+ pos = pos.multiply((double)lacunarity, (double)lacunarity, (double)lacunarity);
+ amplitude *= gain;
+ }
+ }
+
+ return y;
+ }
+
+ public static float getSwirl(Vec3 position, Level level, float sampleSize) {
+ Vec3 sample1Z = getWind(position.add((double)0.0F, (double)0.0F, (double)sampleSize), level).normalize();
+ Vec3 sample2Z = getWind(position.add((double)0.0F, (double)0.0F, (double)(-sampleSize)), level).normalize();
+ Vec3 sample1X = getWind(position.add((double)(-sampleSize), (double)0.0F, (double)0.0F), level).normalize();
+ Vec3 sample2X = getWind(position.add((double)sampleSize, (double)0.0F, (double)0.0F), level).normalize();
+ double compZ = (-sample1Z.dot(sample2Z) + (double)1.0F) / (double)2.0F;
+ double compX = (-sample1X.dot(sample2X) + (double)1.0F) / (double)2.0F;
+ return (float)(compZ * compX);
+ }
+
+ public static Vec3 getWind(Vec3 position, Level level) {
+ return getWind(position, level, false, false, true, false);
+ }
+
+ public static Vec3 getWind(Vec3 position, Level level, boolean ignoreStorms, boolean ignoreTornadoes, boolean windCheck) {
+ return getWind(position, level, ignoreStorms, ignoreTornadoes, windCheck, false);
+ }
+
+ public static Vec3 getWind(Vec3 position, Level level, boolean ignoreStorms, boolean ignoreTornadoes, boolean windCheck, boolean windAnyway) {
+ Vec3 wind = Vec3.ZERO;
+ Vec3 rawWind = Vec3.ZERO;
+ BlockPos blockPos = new BlockPos((int)position.x, (int)position.y, (int)position.z);
+ List tornadicStorms = new ArrayList();
+ if (level == null) {
+ PMWeather.LOGGER.warn("Level is null");
+ return wind;
+ } else {
+ int worldHeight = level.getHeightmapPos(Types.MOTION_BLOCKING, blockPos).getY();
+ if (windCheck && !windAnyway) {
+ if (!Util.canWindAffect(position, level)) {
+ return wind;
+ }
+ } else if (!windAnyway && position.y < (double)worldHeight) {
+ return wind;
+ }
+
+ if (simplexNoise != null) {
+ float timeScale = 20000.0F;
+ float scale = 12000.0F;
+ double ang = FBM(new Vec3(position.x / (double)(scale * 3.0F), position.z / (double)(scale * 3.0F), (double)((float)level.getGameTime() / (timeScale * 6.0F))), 5, 2.0F, 0.1F, 1.0F);
+ ang *= Math.PI;
+ Vec3 dir = (new Vec3(Math.cos(ang), (double)0.0F, Math.sin(ang))).normalize();
+ double speed = Math.max(simplexNoise.getValue(-position.z / (double)scale, -position.x / (double)scale, (double)(-((float)level.getGameTime()) / timeScale)) + (double)1.0F, (double)0.0F) * (double)10.0F;
+ wind = wind.add(dir.multiply(speed, speed, speed));
+ WeatherHandler weatherHandler;
+ if (level.isClientSide()) {
+ weatherHandler = GameBusClientEvents.weatherHandler;
+ } else {
+ weatherHandler = GameBusEvents.MANAGERS.get(level.dimension());
+ }
+
+ if (weatherHandler != null && !ignoreStorms) {
+ for(Storm storm : weatherHandler.getStorms()) {
+ if (!storm.visualOnly) {
+ if (storm.stage >= 3 && storm.stormType == 0) {
+ tornadicStorms.add(storm);
+ }
+
+ double distance = position.multiply((double)1.0F, (double)0.0F, (double)1.0F).distanceTo(storm.position.multiply((double)1.0F, (double)0.0F, (double)1.0F));
+ if (storm.stormType == 2) {
+ Vec3 relativePos = position.subtract(storm.position);
+ Vec3 inward = (new Vec3(-relativePos.x, (double)0.0F, -relativePos.z)).normalize();
+ Vec3 rotational = (new Vec3(relativePos.z, (double)0.0F, -relativePos.x)).normalize();
+ double pullStrngth = (double)((float)storm.windspeed * 0.3F);
+ double rotStrngth = (double)((float)storm.windspeed * 0.7F);
+ float mult = (float)Math.pow((double)1.0F - Math.clamp(distance / (double)storm.maxWidth, (double)0.0F, (double)1.0F), (double)3.0F);
+ if (distance < (double)((float)storm.maxWidth * 0.1F)) {
+ mult += (float)Math.pow((double)1.0F - Math.clamp(distance / (double)((float)storm.maxWidth * 0.1F), (double)0.0F, (double)1.0F), (double)0.75F) * 0.15F;
+ mult = Math.clamp(mult, 0.0F, 1.0F);
+ }
+
+ double d = (double)((float)storm.maxWidth / (1.5F + (float)storm.windspeed / 30.0F));
+ float effect = Math.clamp((float)distance / (float)storm.maxWidth, 0.0F, 1.0F);
+ float noiseX = (float)FBM(new Vec3(position.x / (double)((float)storm.maxWidth * 0.5F), position.z / (double)((float)storm.maxWidth * 0.5F), (double)((float)level.getGameTime() / timeScale)), 5, 2.0F, 0.2F, 1.0F);
+ float noiseZ = (float)FBM(new Vec3(position.z / (double)((float)storm.maxWidth * 0.5F), position.x / (double)((float)storm.maxWidth * 0.5F), (double)((float)level.getGameTime() / timeScale)), 5, 2.0F, 0.2F, 1.0F);
+ relativePos = relativePos.add(new Vec3((double)(noiseX * (float)storm.maxWidth * 0.3F * effect), (double)0.0F, (double)(noiseZ * (float)storm.maxWidth * 0.3F * effect)));
+ double angle = Math.atan2(relativePos.z, relativePos.x) - distance / d;
+ float bands = (float)Math.sin((angle + Math.toRadians((double)((float)storm.tickCount / 8.0F))) * (double)4.0F);
+ mult += Mth.lerp(1.0F - Math.clamp((float)distance / ((float)storm.maxWidth * 0.35F), 0.0F, 1.0F), (float)Math.pow((double)Math.abs(bands), (double)2.0F) * 0.5F * mult, 0.5F * mult);
+ float noise = (float)FBM(new Vec3(position.x / (double)((float)storm.maxWidth * 0.5F), position.z / (double)((float)storm.maxWidth * 0.5F), (double)((float)level.getGameTime() / timeScale)), 5, 2.0F, 0.2F, 1.0F);
+ mult *= Math.clamp(noise, 0.0F, 1.0F) * 0.2F + 0.8F;
+ float noise2 = (float)FBM(new Vec3(position.x / (double)((float)storm.maxWidth * 0.1F), position.z / (double)((float)storm.maxWidth * 0.1F), (double)((float)level.getGameTime() / timeScale)), 5, 2.0F, 0.2F, 1.0F);
+ mult *= Math.clamp(noise2, 0.0F, 1.0F) * 0.1F + 0.9F;
+ mult *= 1.15F + (float)Math.pow((double)1.0F - Math.clamp((distance - (double)((float)storm.maxWidth * 0.1F)) / (double)((float)storm.maxWidth * 0.1F), (double)0.0F, (double)1.0F), (double)2.5F) * 0.35F;
+ float eye = (float)Math.pow(Math.clamp(distance / (double)((float)storm.maxWidth * 0.1F), (double)0.0F, (double)1.0F), (double)Mth.lerp(Math.clamp((float)storm.windspeed / 120.0F, 0.0F, 1.0F), 0.5F, 4.0F));
+ mult *= Mth.lerp((float)Math.pow((double)Math.clamp((float)storm.windspeed / 65.0F, 0.0F, 1.0F), (double)2.0F), 1.0F, eye);
+ Vec3 vec = inward.multiply(pullStrngth, (double)0.0F, pullStrngth).add(rotational.multiply(rotStrngth, (double)0.0F, rotStrngth)).multiply((double)mult, (double)0.0F, (double)mult);
+ if (vec.length() > (double)storm.windspeed) {
+ double dif = vec.length() - (double)storm.windspeed;
+ vec = vec.subtract(new Vec3(dif, (double)0.0F, dif));
+ }
+
+ rawWind = rawWind.add(vec);
+
+ for(Vorticy vorticy : storm.vorticies) {
+ Vec3 pos = vorticy.getPosition();
+ Vec3 rPos = position.subtract(pos);
+ Vec3 in = (new Vec3(-rPos.x, (double)0.0F, -rPos.z)).normalize();
+ Vec3 rot = (new Vec3(rPos.z, (double)0.0F, -rPos.x)).normalize();
+ float width = vorticy.getWidth();
+ double dist = position.multiply((double)1.0F, (double)0.0F, (double)1.0F).distanceTo(pos.multiply((double)1.0F, (double)0.0F, (double)1.0F));
+ double pullStrn = (double)(vorticy.windspeedMult * (float)storm.windspeed * 0.3F);
+ double rotStrn = (double)(vorticy.windspeedMult * (float)storm.windspeed * 0.7F);
+ float m = (float)Math.pow((double)(1.0F - Math.clamp((float)dist / width, 0.0F, 1.0F)), (double)3.75F);
+ m *= Math.clamp((float)dist / (width * 0.1F), 0.0F, 1.0F);
+ m *= 7.0F;
+ Vec3 v = in.multiply(pullStrn, (double)0.0F, pullStrn).add(rot.multiply(rotStrn, (double)0.0F, rotStrn)).multiply((double)m, (double)0.0F, (double)m);
+ rawWind = rawWind.add(v);
+ }
+ }
+
+ if (storm.stormType == 1) {
+ Vec2 v2fWorldPos = new Vec2((float)position.x, (float)position.z);
+ Vec2 stormVel = new Vec2((float)storm.velocity.x, (float)storm.velocity.z);
+ Vec2 v2fStormPos = new Vec2((float)storm.position.x, (float)storm.position.z);
+ Vec2 right = (new Vec2(stormVel.y, -stormVel.x)).normalized();
+ Vec2 fwd = stormVel.normalized();
+ Vec2 le = Util.mulVec2(right, -((float)ServerConfig.stormSize) * 5.0F);
+ Vec2 ri = Util.mulVec2(right, (float)ServerConfig.stormSize * 5.0F);
+ Vec2 off = Util.mulVec2(fwd, -((float)Math.pow(Mth.clamp(distance / (double)((float)ServerConfig.stormSize * 5.0F), (double)0.0F, (double)1.0F), (double)2.0F)) * (float)ServerConfig.stormSize * 1.5F);
+ le = le.add(off);
+ ri = ri.add(off);
+ le = le.add(v2fStormPos);
+ ri = ri.add(v2fStormPos);
+ float d = Util.minimumDistance(le, ri, v2fWorldPos);
+ Vec2 nearPoint = Util.nearestPoint(le, ri, v2fWorldPos);
+ Vec2 facing = v2fWorldPos.add(nearPoint.negated());
+ float behind = -facing.dot(fwd);
+ behind += (float)FBM(new Vec3(position.x / (ServerConfig.stormSize * (double)2.0F), position.z / (ServerConfig.stormSize * (double)2.0F), (double)((float)level.getGameTime() / timeScale)), 5, 2.0F, 0.2F, 1.0F) * (float)ServerConfig.stormSize * 0.25F;
+ float perc = 0.0F;
+ float sze = (float)ServerConfig.stormSize * 4.0F;
+ behind += (float)ServerConfig.stormSize;
+ if (behind > 0.0F) {
+ float p = Mth.clamp(Math.abs(behind) / sze, 0.0F, 1.0F);
+ float start = 0.06F;
+ if (storm.stage >= 3) {
+ start = Mth.lerp((float)storm.energy / 100.0F, start, start * 2.5F);
+ }
+
+ if (p <= start) {
+ p /= start;
+ } else {
+ p = 1.0F - (p - start) / (1.0F - start);
+ if (storm.stage >= 3) {
+ p = (float)Math.pow((double)p, (double)Mth.lerp((float)storm.energy / 100.0F, 1.0F, 0.75F));
+ }
+ }
+
+ perc = Mth.clamp(p, 0.0F, 1.0F);
+ }
+
+ if (storm.stage < 1) {
+ perc *= (float)storm.energy / 100.0F;
+ } else if (storm.stage == 1) {
+ perc *= (float)storm.energy / 125.0F + 1.0F;
+ } else if (storm.stage == 2) {
+ perc *= (float)storm.energy / 200.0F + 1.8F;
+ } else {
+ perc *= (float)storm.energy / 100.0F + 2.3F;
+ }
+
+ float gustNoise = (float)FBM(new Vec3(position.z / (ServerConfig.stormSize * (double)2.0F), position.x / (ServerConfig.stormSize * (double)2.0F), (double)((float)level.getGameTime() / timeScale)), 7, 2.0F, 0.4F, 1.0F);
+ if (storm.stage >= 3) {
+ float p = (float)storm.energy / 100.0F;
+ gustNoise *= 1.0F - p;
+ perc *= 1.0F + p * 0.3F;
+ }
+
+ perc *= Mth.lerp(Mth.clamp(behind / ((float)ServerConfig.stormSize * 3.0F), 0.0F, 1.0F), (float)Math.pow((double)(0.8F + gustNoise * 0.5F), (double)1.5F), 0.5F);
+ perc *= Mth.sqrt(1.0F - Mth.clamp(d / sze, 0.0F, 1.0F));
+ wind = wind.add(storm.velocity.multiply((double)(perc * 13.0F) * ServerConfig.squallStrengthMultiplier, (double)0.0F, (double)(perc * 13.0F) * ServerConfig.squallStrengthMultiplier));
+ }
+
+ if (storm.stormType == 0) {
+ Vec3 relativePos = position.subtract(storm.position);
+ Vec3 inward = (new Vec3(-relativePos.x, (double)0.0F, -relativePos.z)).normalize();
+ Vec3 rotational = (new Vec3(relativePos.z, (double)0.0F, -relativePos.x)).normalize();
+ double pullStrngth = (double)1.0F - Math.clamp(distance / (ServerConfig.stormSize * (double)4.0F), (double)0.0F, (double)1.0F);
+ double rotStrngth = (double)1.0F - Math.clamp(distance / ServerConfig.stormSize, (double)0.0F, (double)1.0F);
+ if (storm.stage < 1) {
+ pullStrngth *= (double)0.5F;
+ pullStrngth *= (double)((float)storm.energy / 100.0F);
+ rotStrngth *= (double)0.0F;
+ } else if (storm.stage == 1) {
+ pullStrngth *= (double)((float)storm.energy / 200.0F + 0.5F);
+ rotStrngth *= (double)((float)storm.energy / 100.0F * 0.1F);
+ } else if (storm.stage == 2) {
+ pullStrngth *= (double)(1.0F + (float)storm.energy / 100.0F);
+ rotStrngth *= (double)(0.1F + (float)storm.energy / 100.0F * 0.4F);
+ } else {
+ pullStrngth *= (double)(2.0F + (float)storm.windspeed / 400.0F);
+ rotStrngth *= (double)(0.5F + (float)storm.windspeed / 400.0F);
+ }
+
+ pullStrngth *= (double)0.5F;
+ rotStrngth *= (double)6.0F;
+ Vec3 vec = inward.multiply(pullStrngth, (double)0.0F, pullStrngth).add(rotational.multiply(rotStrngth, (double)0.0F, rotStrngth)).multiply((double)20.0F, (double)20.0F, (double)20.0F);
+ wind = wind.add(vec);
+ }
+ }
+ }
+ }
+ }
+
+ if (wind.length() > (double)30.0F) {
+ double over = wind.length() - (double)40.0F;
+ double val = (double)30.0F + over / (double)3.0F;
+ wind = wind.normalize().multiply(val, val, val);
+ }
+
+ if (blockPos.getY() > 85) {
+ float val = Math.clamp((float)(blockPos.getY() - 85) / 40.0F, 0.0F, 1.0F) / 3.0F + 1.0F;
+ wind = wind.multiply((double)val, (double)val, (double)val);
+ }
+
+ wind = wind.add(rawWind);
+ int heightAbove = blockPos.getY() - worldHeight;
+ if (heightAbove > 0) {
+ float val = Math.clamp((float)heightAbove / 15.0F, 0.0F, 1.0F) / 3.0F + 1.0F;
+ wind = wind.multiply((double)val, (double)val, (double)val);
+ }
+
+ float tornadicEffect = 0.0F;
+ Vec3 tornadicWind = Vec3.ZERO;
+ if (!ignoreStorms && !ignoreTornadoes) {
+ for(Storm tornadicStorm : tornadicStorms) {
+ Vec3 relativePos = position.subtract(tornadicStorm.position);
+ Vec3 inward = (new Vec3(-relativePos.x, (double)0.0F, -relativePos.z)).normalize();
+ Vec3 rotational = (new Vec3(relativePos.z, (double)0.0F, -relativePos.x)).normalize();
+ double distance = position.distanceTo(tornadicStorm.position);
+ if (!(distance > (double)(tornadicStorm.width * 2.0F))) {
+ double windEffect = (double)tornadicStorm.getWind(position);
+ tornadicEffect = Math.clamp((float)windEffect / (float)Math.max(tornadicStorm.windspeed, 30), tornadicEffect, 1.0F);
+ if (Float.isNaN(tornadicEffect)) {
+ tornadicEffect = 0.0F;
+ }
+
+ double inPerc = 0.35;
+ tornadicWind = tornadicWind.add(inward.multiply(windEffect * inPerc, windEffect * inPerc, windEffect * inPerc)).add(rotational.add(windEffect * ((double)1.0F - inPerc), windEffect * ((double)1.0F - inPerc), windEffect * ((double)1.0F - inPerc)));
+ }
+ }
+ }
+
+ return wind.lerp(tornadicWind, (double)tornadicEffect);
+ }
+ }
+
+ public static Vec3 getWind(BlockPos position, Level level, boolean ignoreStorms, boolean ignoreTornadoes, boolean windCheck) {
+ return getWind(new Vec3((double)position.getX(), (double)(position.getY() + 1), (double)position.getZ()), level, ignoreStorms, ignoreTornadoes, windCheck, false);
+ }
+
+ public static Vec3 getWind(BlockPos position, Level level) {
+ return getWind(new Vec3((double)position.getX(), (double)(position.getY() + 1), (double)position.getZ()), level, false, false, true, false);
+ }
+}
diff --git a/pmweather_tornado_extraction_export/pmweather.mixins.json b/pmweather_tornado_extraction_export/pmweather.mixins.json
new file mode 100644
index 00000000..88096ef8
--- /dev/null
+++ b/pmweather_tornado_extraction_export/pmweather.mixins.json
@@ -0,0 +1,21 @@
+{
+ "required": true,
+ "package": "dev.protomanly.pmweather.mixin",
+ "compatibilityLevel": "JAVA_21",
+ "refmap": "pmweather.refmap.json",
+ "mixins": [
+ "LevelMixin",
+ "ServerLevelMixin",
+ "SnowLayerBlockMixin",
+ "SpreadingSnowyDirtBlockMixin"
+ ],
+ "client": [
+ "ParticleMixin",
+ "LevelRendererMixin",
+ "PostChainMixin",
+ "ClientLevelMixin"
+ ],
+ "injectors": {
+ "defaultRequire": 1
+ }
+}
\ No newline at end of file
diff --git a/run-data/logs/2026-03-17-2.log.gz b/run-data/logs/2026-03-17-2.log.gz
new file mode 100644
index 00000000..7879fc01
Binary files /dev/null and b/run-data/logs/2026-03-17-2.log.gz differ
diff --git a/run-data/logs/2026-05-12-1.log.gz b/run-data/logs/2026-05-12-1.log.gz
new file mode 100644
index 00000000..ff430f54
Binary files /dev/null and b/run-data/logs/2026-05-12-1.log.gz differ
diff --git a/run-data/logs/2026-05-12-2.log.gz b/run-data/logs/2026-05-12-2.log.gz
new file mode 100644
index 00000000..919d4f07
Binary files /dev/null and b/run-data/logs/2026-05-12-2.log.gz differ
diff --git a/run-data/logs/debug-1.log.gz b/run-data/logs/debug-1.log.gz
index 82a90596..ab0238ac 100644
Binary files a/run-data/logs/debug-1.log.gz and b/run-data/logs/debug-1.log.gz differ
diff --git a/run-data/logs/debug-2.log.gz b/run-data/logs/debug-2.log.gz
index 37b9c7fa..546a8a1b 100644
Binary files a/run-data/logs/debug-2.log.gz and b/run-data/logs/debug-2.log.gz differ
diff --git a/run-data/logs/debug-3.log.gz b/run-data/logs/debug-3.log.gz
index 65f89ec0..fd4a7f23 100644
Binary files a/run-data/logs/debug-3.log.gz and b/run-data/logs/debug-3.log.gz differ
diff --git a/run-data/logs/debug-4.log.gz b/run-data/logs/debug-4.log.gz
new file mode 100644
index 00000000..82a90596
Binary files /dev/null and b/run-data/logs/debug-4.log.gz differ
diff --git a/run-data/logs/debug-5.log.gz b/run-data/logs/debug-5.log.gz
new file mode 100644
index 00000000..37b9c7fa
Binary files /dev/null and b/run-data/logs/debug-5.log.gz differ
diff --git a/run-data/logs/debug.log b/run-data/logs/debug.log
index 3ff35303..915ce5a9 100644
--- a/run-data/logs/debug.log
+++ b/run-data/logs/debug.log
@@ -1,819 +1,809 @@
-[24nov.2025 23:48:47.382] [main/INFO] [cpw.mods.modlauncher.Launcher/MODLAUNCHER]: ModLauncher running: args [--launchTarget, forgedatauserdev, --assetIndex, 5, --assetsDir, C:\Users\matga\.gradle\caches\forge_gradle\assets, --gameDir, ., --fml.forgeVersion, 47.4.9, --fml.mcVersion, 1.20.1, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20230612.114412, --mod, projectatmosphere, --all, --output, G:\Project-Atmosphere\src\generated\resources, --existing, G:\Project-Atmosphere\src\main\resources, --mixin.config, projectatmosphere.mixins.json]
-[24nov.2025 23:48:47.390] [main/INFO] [cpw.mods.modlauncher.Launcher/MODLAUNCHER]: ModLauncher 10.0.9+10.0.9+main.dcd20f30 starting: java version 17.0.15 by Eclipse Adoptium; OS Windows 11 arch amd64 version 10.0
-[24nov.2025 23:48:47.440] [main/DEBUG] [cpw.mods.modlauncher.LaunchServiceHandler/MODLAUNCHER]: Found launch services [fmlclientdev,forgeclient,minecraft,forgegametestserverdev,fmlserveruserdev,fmlclient,fmldatauserdev,forgeserverdev,forgeserveruserdev,forgeclientdev,forgeclientuserdev,forgeserver,forgedatadev,fmlserver,fmlclientuserdev,fmlserverdev,forgedatauserdev,testharness,forgegametestserveruserdev]
-[24nov.2025 23:48:47.456] [main/DEBUG] [cpw.mods.modlauncher.NameMappingServiceHandler/MODLAUNCHER]: Found naming services : [srgtomcp]
-[24nov.2025 23:48:47.466] [main/DEBUG] [cpw.mods.modlauncher.LaunchPluginHandler/MODLAUNCHER]: Found launch plugins: [mixin,eventbus,slf4jfixer,object_holder_definalize,runtime_enum_extender,capability_token_subclass,accesstransformer,runtimedistcleaner]
-[24nov.2025 23:48:47.473] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Discovering transformation services
-[24nov.2025 23:48:47.476] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path GAMEDIR is G:\Project-Atmosphere\run-data
-[24nov.2025 23:48:47.476] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path MODSDIR is G:\Project-Atmosphere\run-data\mods
-[24nov.2025 23:48:47.477] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path CONFIGDIR is G:\Project-Atmosphere\run-data\config
-[24nov.2025 23:48:47.477] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path FMLCONFIG is G:\Project-Atmosphere\run-data\config\fml.toml
-[24nov.2025 23:48:47.500] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Found additional transformation services from discovery services:
-[24nov.2025 23:48:47.503] [main/INFO] [net.minecraftforge.fml.loading.ImmediateWindowHandler/]: ImmediateWindowProvider not loading because launch target is forgedatauserdev
-[24nov.2025 23:48:47.508] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Found transformer services : [mixin,fml]
-[24nov.2025 23:48:47.508] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Transformation services loading
-[24nov.2025 23:48:47.508] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Loading service mixin
-[24nov.2025 23:48:47.508] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Loaded service mixin
-[24nov.2025 23:48:47.508] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Loading service fml
-[24nov.2025 23:48:47.509] [main/DEBUG] [net.minecraftforge.fml.loading.LauncherVersion/CORE]: Found FMLLauncher version 1.0
-[24nov.2025 23:48:47.509] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML 1.0 loading
-[24nov.2025 23:48:47.509] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML found ModLauncher version : 10.0.9+10.0.9+main.dcd20f30
-[24nov.2025 23:48:47.510] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Requesting CoreMods to not apply the fix for ASMAPI.findFirstInstructionBefore by default
-[24nov.2025 23:48:47.510] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML found AccessTransformer version : 8.0.4+66+master.c09db6d7
-[24nov.2025 23:48:47.510] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML found EventBus version : 6.0.5+6.0.5+master.eb8e549b
-[24nov.2025 23:48:47.510] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Found Runtime Dist Cleaner
-[24nov.2025 23:48:47.511] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/]: CoreMods will preserve legacy behavior of ASMAPI.findFirstInstructionBefore for backwards-compatibility
-[24nov.2025 23:48:47.511] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML found CoreMod version : 5.2.4
-[24nov.2025 23:48:47.512] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Found ForgeSPI package implementation version 7.0.1+7.0.1+master.d2b38bf6
-[24nov.2025 23:48:47.512] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Found ForgeSPI package specification 5
-[24nov.2025 23:48:47.512] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Loaded service fml
-[24nov.2025 23:48:47.512] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Configuring option handling for services
-[24nov.2025 23:48:47.517] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Transformation services initializing
-[24nov.2025 23:48:47.517] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initializing transformation service mixin
-[24nov.2025 23:48:47.525] [main/DEBUG] [mixin/]: MixinService [ModLauncher] was successfully booted in cpw.mods.cl.ModuleClassLoader@6ca8564a
-[24nov.2025 23:48:47.534] [main/INFO] [mixin/]: SpongePowered MIXIN Subsystem Version=0.8.5 Source=union:/C:/Users/matga/.gradle/caches/modules-2/files-2.1/org.spongepowered/mixin/0.8.5/9d1c0c3a304ae6697ecd477218fa61b850bf57fc/mixin-0.8.5.jar%23132!/ Service=ModLauncher Env=CLIENT
-[24nov.2025 23:48:47.536] [main/DEBUG] [mixin/]: Initialising Mixin Platform Manager
-[24nov.2025 23:48:47.536] [main/DEBUG] [mixin/]: Adding mixin platform agents for container ModLauncher Root Container(ModLauncher:4f56a0a2)
-[24nov.2025 23:48:47.537] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for ModLauncher Root Container(ModLauncher:4f56a0a2)
-[24nov.2025 23:48:47.537] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container ModLauncher Root Container(ModLauncher:4f56a0a2)
-[24nov.2025 23:48:47.537] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for ModLauncher Root Container(ModLauncher:4f56a0a2)
-[24nov.2025 23:48:47.537] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container ModLauncher Root Container(ModLauncher:4f56a0a2)
-[24nov.2025 23:48:47.538] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initialized transformation service mixin
-[24nov.2025 23:48:47.540] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initializing transformation service fml
-[24nov.2025 23:48:47.540] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Setting up basic FML game directories
-[24nov.2025 23:48:47.540] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path GAMEDIR is G:\Project-Atmosphere\run-data
-[24nov.2025 23:48:47.540] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path MODSDIR is G:\Project-Atmosphere\run-data\mods
-[24nov.2025 23:48:47.540] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path CONFIGDIR is G:\Project-Atmosphere\run-data\config
-[24nov.2025 23:48:47.540] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path FMLCONFIG is G:\Project-Atmosphere\run-data\config\fml.toml
-[24nov.2025 23:48:47.541] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Loading configuration
-[24nov.2025 23:48:47.549] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Preparing ModFile
-[24nov.2025 23:48:47.551] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Preparing launch handler
-[24nov.2025 23:48:47.551] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Using forgedatauserdev as launch service
-[24nov.2025 23:48:47.558] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Received command line version data : VersionInfo[forgeVersion=47.4.9, mcVersion=1.20.1, mcpVersion=20230612.114412, forgeGroup=net.minecraftforge]
-[24nov.2025 23:48:47.558] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initialized transformation service fml
-[24nov.2025 23:48:47.559] [main/DEBUG] [cpw.mods.modlauncher.NameMappingServiceHandler/MODLAUNCHER]: Current naming domain is 'mcp'
-[24nov.2025 23:48:47.559] [main/DEBUG] [cpw.mods.modlauncher.NameMappingServiceHandler/MODLAUNCHER]: Identified name mapping providers {srg=srgtomcp:1234}
-[24nov.2025 23:48:47.559] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Transformation services begin scanning
-[24nov.2025 23:48:47.560] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Beginning scan trigger - transformation service mixin
-[24nov.2025 23:48:47.560] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: End scan trigger - transformation service mixin
-[24nov.2025 23:48:47.560] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Beginning scan trigger - transformation service fml
-[24nov.2025 23:48:47.560] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Initiating mod scan
-[24nov.2025 23:48:47.566] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModListHandler/CORE]: Found mod coordinates from lists: []
-[24nov.2025 23:48:47.569] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer/CORE]: Found Mod Locators : (mods folder:null),(maven libs:null),(exploded directory:null),(minecraft:null),(userdev classpath:null)
-[24nov.2025 23:48:47.569] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer/CORE]: Found Dependency Locators : (JarInJar:null)
-[24nov.2025 23:48:47.573] [main/DEBUG] [net.minecraftforge.fml.loading.targets.CommonLaunchHandler/CORE]: Got mod coordinates projectatmosphere%%G:\Project-Atmosphere\build\resources\main;projectatmosphere%%G:\Project-Atmosphere\build\classes\java\main from env
-[24nov.2025 23:48:47.573] [main/DEBUG] [net.minecraftforge.fml.loading.targets.CommonLaunchHandler/CORE]: Found supplied mod coordinates [{projectatmosphere=[G:\Project-Atmosphere\build\resources\main, G:\Project-Atmosphere\build\classes\java\main]}]
-[24nov.2025 23:48:47.785] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar with {minecraft} mods - versions {1.20.1}
-[24nov.2025 23:48:47.788] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\javafmllanguage\1.20.1-47.4.9\6802c410879d06cd2c16ac24988dd5893293e21c\javafmllanguage-1.20.1-47.4.9.jar
-[24nov.2025 23:48:47.788] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\javafmllanguage\1.20.1-47.4.9\6802c410879d06cd2c16ac24988dd5893293e21c\javafmllanguage-1.20.1-47.4.9.jar is missing mods.toml file
-[24nov.2025 23:48:47.790] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\lowcodelanguage\1.20.1-47.4.9\1a098871d069f3cb2ed12f392f96f462902c0350\lowcodelanguage-1.20.1-47.4.9.jar
-[24nov.2025 23:48:47.790] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\lowcodelanguage\1.20.1-47.4.9\1a098871d069f3cb2ed12f392f96f462902c0350\lowcodelanguage-1.20.1-47.4.9.jar is missing mods.toml file
-[24nov.2025 23:48:47.792] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\mclanguage\1.20.1-47.4.9\5279b751da7297817db4fa9729e3f3c7bf03d594\mclanguage-1.20.1-47.4.9.jar
-[24nov.2025 23:48:47.792] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\mclanguage\1.20.1-47.4.9\5279b751da7297817db4fa9729e3f3c7bf03d594\mclanguage-1.20.1-47.4.9.jar is missing mods.toml file
-[24nov.2025 23:48:47.793] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\fmlcore\1.20.1-47.4.9\f4f03c369d155fb551ebf908db260a0c5600c29b\fmlcore-1.20.1-47.4.9.jar
-[24nov.2025 23:48:47.795] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\fmlcore\1.20.1-47.4.9\f4f03c369d155fb551ebf908db260a0c5600c29b\fmlcore-1.20.1-47.4.9.jar is missing mods.toml file
-[24nov.2025 23:48:47.800] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\build\resources\main
-[24nov.2025 23:48:47.810] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file main with {projectatmosphere} mods - versions {0.6.0.0-pre2}
-[24nov.2025 23:48:47.812] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate /
-[24nov.2025 23:48:47.814] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file with {forge} mods - versions {47.4.9}
-[24nov.2025 23:48:47.828] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\libs\tectonic-3.0.17-forge-1.20.1-dev.jar
-[24nov.2025 23:48:47.828] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-3.0.17-forge-1.20.1-dev.jar with {tectonic} mods - versions {3.0.17}
-[24nov.2025 23:48:47.831] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\simpleclouds\0.7.3+1.20.1-forge_mapped_official_1.20.1\simpleclouds-0.7.3+1.20.1-forge_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.832] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file simpleclouds-0.7.3+1.20.1-forge_mapped_official_1.20.1.jar with {simpleclouds} mods - versions {0.7.3+1.20.1-forge}
-[24nov.2025 23:48:47.832] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.833] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar with {crackerslib} mods - versions {1.20.1-0.4.6}
-[24nov.2025 23:48:47.834] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\betterdays-895618\7013807_mapped_official_1.20.1\betterdays-895618-7013807_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.835] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file betterdays-895618-7013807_mapped_official_1.20.1.jar with {betterdays} mods - versions {3.3.4.4}
-[24nov.2025 23:48:47.836] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-plus-1288843\7174860_mapped_official_1.20.1\serene-seasons-plus-1288843-7174860_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.837] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-plus-1288843-7174860_mapped_official_1.20.1.jar with {sereneseasonsplus} mods - versions {4.1.0}
-[24nov.2025 23:48:47.837] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\rainboows-1121832\6421980_mapped_official_1.20.1\rainboows-1121832-6421980_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.842] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file rainboows-1121832-6421980_mapped_official_1.20.1.jar with {rainbows} mods - versions {1.5}
-[24nov.2025 23:48:47.843] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\auroras-1105290\6040671_mapped_official_1.20.1\auroras-1105290-6040671_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.844] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file auroras-1105290-6040671_mapped_official_1.20.1.jar with {auroras} mods - versions {1.6.2}
-[24nov.2025 23:48:47.845] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\io.github.xgabou\gaboulibs_forge\1.2\7247c49d39f07ab149de67aa345e8cf65978be1c\gaboulibs_forge-1.2.jar
-[24nov.2025 23:48:47.846] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file gaboulibs_forge-1.2.jar with {gaboulibs} mods - versions {1.2}
-[24nov.2025 23:48:47.847] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\architectury-api-419699\5137938_mapped_official_1.20.1\architectury-api-419699-5137938_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.848] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file architectury-api-419699-5137938_mapped_official_1.20.1.jar with {architectury} mods - versions {9.2.14}
-[24nov.2025 23:48:47.849] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\tectonic-686836\7197834_mapped_official_1.20.1\tectonic-686836-7197834_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.849] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-686836-7197834_mapped_official_1.20.1.jar with {tectonic} mods - versions {3.0.17}
-[24nov.2025 23:48:47.851] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\lithostitched-936015\6742615_mapped_official_1.20.1\lithostitched-936015-6742615_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.851] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file lithostitched-936015-6742615_mapped_official_1.20.1.jar with {lithostitched} mods - versions {1.4.11}
-[24nov.2025 23:48:47.852] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-291874\6398227_mapped_official_1.20.1\serene-seasons-291874-6398227_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.853] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-291874-6398227_mapped_official_1.20.1.jar with {sereneseasons} mods - versions {9.1.0.2}
-[24nov.2025 23:48:47.855] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\glitchcore-955399\5787839_mapped_official_1.20.1\glitchcore-955399-5787839_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.855] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file glitchcore-955399-5787839_mapped_official_1.20.1.jar with {glitchcore} mods - versions {0.0.1.1}
-[24nov.2025 23:48:47.856] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\spark-361579\4738952_mapped_official_1.20.1\spark-361579-4738952_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.857] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file spark-361579-4738952_mapped_official_1.20.1.jar with {spark} mods - versions {1.10.53}
-[24nov.2025 23:48:47.858] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\cloth-config-348521\5729105_mapped_official_1.20.1\cloth-config-348521-5729105_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.858] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file cloth-config-348521-5729105_mapped_official_1.20.1.jar with {cloth_config} mods - versions {11.1.136}
-[24nov.2025 23:48:47.859] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\chloride-931925\6615986_mapped_official_1.20.1\chloride-931925-6615986_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.860] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file chloride-931925-6615986_mapped_official_1.20.1.jar with {chloride} mods - versions {1.7.2}
-[24nov.2025 23:48:47.862] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\embeddium-908741\5681725_mapped_official_1.20.1\embeddium-908741-5681725_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.862] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file embeddium-908741-5681725_mapped_official_1.20.1.jar with {embeddium,rubidium} mods - versions {0.3.31+mc1.20.1,0.7.1}
-[24nov.2025 23:48:47.864] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\mezz\jei\jei-1.20.1-forge\15.8.2.26_mapped_official_1.20.1\jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.865] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar with {jei} mods - versions {15.8.2.26}
-[24nov.2025 23:48:47.866] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\jade-324717\6271651_mapped_official_1.20.1\jade-324717-6271651_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.867] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jade-324717-6271651_mapped_official_1.20.1.jar with {jade} mods - versions {11.13.1+forge}
-[24nov.2025 23:48:47.868] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\dev.architectury\architectury-forge\9.2.14\f48e657815d2e540d63bd162238631ed3c5633d3\architectury-forge-9.2.14.jar
-[24nov.2025 23:48:47.868] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file architectury-forge-9.2.14.jar with {architectury} mods - versions {9.2.14}
-[24nov.2025 23:48:47.870] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\libs\tectonic-3.0.17-forge-1.20.1-dev.jar
-[24nov.2025 23:48:47.871] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-3.0.17-forge-1.20.1-dev.jar with {tectonic} mods - versions {3.0.17}
-[24nov.2025 23:48:47.872] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\simpleclouds\0.7.3+1.20.1-forge_mapped_official_1.20.1\simpleclouds-0.7.3+1.20.1-forge_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.872] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file simpleclouds-0.7.3+1.20.1-forge_mapped_official_1.20.1.jar with {simpleclouds} mods - versions {0.7.3+1.20.1-forge}
-[24nov.2025 23:48:47.873] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.874] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar with {crackerslib} mods - versions {1.20.1-0.4.6}
-[24nov.2025 23:48:47.882] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\betterdays-895618\7013807_mapped_official_1.20.1\betterdays-895618-7013807_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.882] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file betterdays-895618-7013807_mapped_official_1.20.1.jar with {betterdays} mods - versions {3.3.4.4}
-[24nov.2025 23:48:47.883] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-plus-1288843\7174860_mapped_official_1.20.1\serene-seasons-plus-1288843-7174860_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.883] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-plus-1288843-7174860_mapped_official_1.20.1.jar with {sereneseasonsplus} mods - versions {4.1.0}
-[24nov.2025 23:48:47.884] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\rainboows-1121832\6421980_mapped_official_1.20.1\rainboows-1121832-6421980_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.885] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file rainboows-1121832-6421980_mapped_official_1.20.1.jar with {rainbows} mods - versions {1.5}
-[24nov.2025 23:48:47.886] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\auroras-1105290\6040671_mapped_official_1.20.1\auroras-1105290-6040671_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.887] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file auroras-1105290-6040671_mapped_official_1.20.1.jar with {auroras} mods - versions {1.6.2}
-[24nov.2025 23:48:47.888] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\io.github.xgabou\gaboulibs_forge\1.2\7247c49d39f07ab149de67aa345e8cf65978be1c\gaboulibs_forge-1.2.jar
-[24nov.2025 23:48:47.888] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file gaboulibs_forge-1.2.jar with {gaboulibs} mods - versions {1.2}
-[24nov.2025 23:48:47.891] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\architectury-api-419699\5137938_mapped_official_1.20.1\architectury-api-419699-5137938_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.891] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file architectury-api-419699-5137938_mapped_official_1.20.1.jar with {architectury} mods - versions {9.2.14}
-[24nov.2025 23:48:47.892] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\tectonic-686836\7197834_mapped_official_1.20.1\tectonic-686836-7197834_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.893] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-686836-7197834_mapped_official_1.20.1.jar with {tectonic} mods - versions {3.0.17}
-[24nov.2025 23:48:47.894] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\lithostitched-936015\6742615_mapped_official_1.20.1\lithostitched-936015-6742615_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.895] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file lithostitched-936015-6742615_mapped_official_1.20.1.jar with {lithostitched} mods - versions {1.4.11}
-[24nov.2025 23:48:47.896] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-291874\6398227_mapped_official_1.20.1\serene-seasons-291874-6398227_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.896] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-291874-6398227_mapped_official_1.20.1.jar with {sereneseasons} mods - versions {9.1.0.2}
-[24nov.2025 23:48:47.898] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\glitchcore-955399\5787839_mapped_official_1.20.1\glitchcore-955399-5787839_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.898] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file glitchcore-955399-5787839_mapped_official_1.20.1.jar with {glitchcore} mods - versions {0.0.1.1}
-[24nov.2025 23:48:47.899] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\spark-361579\4738952_mapped_official_1.20.1\spark-361579-4738952_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.899] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file spark-361579-4738952_mapped_official_1.20.1.jar with {spark} mods - versions {1.10.53}
-[24nov.2025 23:48:47.901] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\cloth-config-348521\5729105_mapped_official_1.20.1\cloth-config-348521-5729105_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.901] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file cloth-config-348521-5729105_mapped_official_1.20.1.jar with {cloth_config} mods - versions {11.1.136}
-[24nov.2025 23:48:47.903] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\chloride-931925\6615986_mapped_official_1.20.1\chloride-931925-6615986_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.904] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file chloride-931925-6615986_mapped_official_1.20.1.jar with {chloride} mods - versions {1.7.2}
-[24nov.2025 23:48:47.905] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\embeddium-908741\5681725_mapped_official_1.20.1\embeddium-908741-5681725_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.906] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file embeddium-908741-5681725_mapped_official_1.20.1.jar with {embeddium,rubidium} mods - versions {0.3.31+mc1.20.1,0.7.1}
-[24nov.2025 23:48:47.907] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\mezz\jei\jei-1.20.1-forge\15.8.2.26_mapped_official_1.20.1\jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.907] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar with {jei} mods - versions {15.8.2.26}
-[24nov.2025 23:48:47.910] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\jade-324717\6271651_mapped_official_1.20.1\jade-324717-6271651_mapped_official_1.20.1.jar
-[24nov.2025 23:48:47.910] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jade-324717-6271651_mapped_official_1.20.1.jar with {jade} mods - versions {11.13.1+forge}
-[24nov.2025 23:48:47.911] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\dev.architectury\architectury-forge\9.2.14\f48e657815d2e540d63bd162238631ed3c5633d3\architectury-forge-9.2.14.jar
-[24nov.2025 23:48:47.912] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file architectury-forge-9.2.14.jar with {architectury} mods - versions {9.2.14}
-[24nov.2025 23:48:47.922] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid sereneseasons, selecting most recent based on version data
-[24nov.2025 23:48:47.922] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file serene-seasons-291874-6398227_mapped_official_1.20.1.jar for modid sereneseasons with version 9.1.0.2
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid betterdays, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file betterdays-895618-7013807_mapped_official_1.20.1.jar for modid betterdays with version 3.3.4.4
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid crackerslib, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar for modid crackerslib with version 1.20.1-0.4.6
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid glitchcore, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file glitchcore-955399-5787839_mapped_official_1.20.1.jar for modid glitchcore with version 0.0.1.1
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid jade, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file jade-324717-6271651_mapped_official_1.20.1.jar for modid jade with version 11.13.1+forge
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid simpleclouds, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file simpleclouds-0.7.3+1.20.1-forge_mapped_official_1.20.1.jar for modid simpleclouds with version 0.7.3+1.20.1-forge
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 4 mods for first modid architectury, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file architectury-api-419699-5137938_mapped_official_1.20.1.jar for modid architectury with version 9.2.14
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid auroras, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file auroras-1105290-6040671_mapped_official_1.20.1.jar for modid auroras with version 1.6.2
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid jei, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar for modid jei with version 15.8.2.26
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid lithostitched, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file lithostitched-936015-6742615_mapped_official_1.20.1.jar for modid lithostitched with version 1.4.11
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid cloth_config, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file cloth-config-348521-5729105_mapped_official_1.20.1.jar for modid cloth_config with version 11.1.136
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid spark, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file spark-361579-4738952_mapped_official_1.20.1.jar for modid spark with version 1.10.53
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid gaboulibs, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file gaboulibs_forge-1.2.jar for modid gaboulibs with version 1.2
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid rainbows, selecting most recent based on version data
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file rainboows-1121832-6421980_mapped_official_1.20.1.jar for modid rainbows with version 1.5
-[24nov.2025 23:48:47.923] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid chloride, selecting most recent based on version data
-[24nov.2025 23:48:47.924] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file chloride-931925-6615986_mapped_official_1.20.1.jar for modid chloride with version 1.7.2
-[24nov.2025 23:48:47.924] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid embeddium, selecting most recent based on version data
-[24nov.2025 23:48:47.924] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file embeddium-908741-5681725_mapped_official_1.20.1.jar for modid embeddium with version 0.3.31+mc1.20.1
-[24nov.2025 23:48:47.924] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid sereneseasonsplus, selecting most recent based on version data
-[24nov.2025 23:48:47.924] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file serene-seasons-plus-1288843-7174860_mapped_official_1.20.1.jar for modid sereneseasonsplus with version 4.1.0
-[24nov.2025 23:48:47.924] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 4 mods for first modid tectonic, selecting most recent based on version data
-[24nov.2025 23:48:47.924] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file tectonic-3.0.17-forge-1.20.1-dev.jar for modid tectonic with version 3.0.17
-[24nov.2025 23:48:47.937] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from serene-seasons-291874-6398227_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from glitchcore-955399-5787839_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from jade-324717-6271651_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from simpleclouds-0.7.3+1.20.1-forge_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from architectury-api-419699-5137938_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from auroras-1105290-6040671_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from lithostitched-936015-6742615_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from cloth-config-348521-5729105_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from spark-361579-4738952_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from gaboulibs_forge-1.2.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from rainboows-1121832-6421980_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.938] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from , it does not contain dependency information.
-[24nov.2025 23:48:47.939] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from simplecloudsapi-forge-1.4-1.20.1_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.939] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from chloride-931925-6615986_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.939] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from main, it does not contain dependency information.
-[24nov.2025 23:48:47.939] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from serene-seasons-plus-1288843-7174860_mapped_official_1.20.1.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.939] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from tectonic-3.0.17-forge-1.20.1-dev.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.939] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from mclanguage-1.20.1-47.4.9.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.939] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from javafmllanguage-1.20.1-47.4.9.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.939] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from fmlcore-1.20.1-47.4.9.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.939] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from lowcodelanguage-1.20.1-47.4.9.jar, it does not contain dependency information.
-[24nov.2025 23:48:47.994] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
-[24nov.2025 23:48:47.995] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file mixinextras-forge-0.3.5.jar with {mixinextras} mods - versions {0.3.5}
-[24nov.2025 23:48:47.998] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
-[24nov.2025 23:48:47.998] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file whitenoise-1.20.1-forge-1.0.0.jar with {whitenoise} mods - versions {1.0.0}
-[24nov.2025 23:48:48.003] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from whitenoise-1.20.1-forge-1.0.0.jar, it does not contain dependency information.
-[24nov.2025 23:48:48.003] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from MixinExtras-0.3.5.jar, it does not contain dependency information.
-[24nov.2025 23:48:48.026] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
-[24nov.2025 23:48:48.026] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file whitenoise-1.20.1-forge-1.0.0.jar with {whitenoise} mods - versions {1.0.0}
-[24nov.2025 23:48:48.030] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
-[24nov.2025 23:48:48.030] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file mixinextras-forge-0.3.5.jar with {mixinextras} mods - versions {0.3.5}
-[24nov.2025 23:48:48.031] [main/INFO] [net.minecraftforge.fml.loading.moddiscovery.JarInJarDependencyLocator/]: Found 3 dependencies adding them to mods collection
-[24nov.2025 23:48:48.032] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar with {minecraft} mods - versions {1.20.1}
-[24nov.2025 23:48:48.033] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\minecraft_user_repo\net\minecraftforge\forge\1.20.1-47.4.9_mapped_official_1.20.1\forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar with languages [LanguageSpec[languageName=minecraft, acceptedVersions=1]]
-[24nov.2025 23:48:48.034] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\betterdays-895618\7013807_mapped_official_1.20.1\betterdays-895618-7013807_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.034] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file betterdays-895618-7013807_mapped_official_1.20.1.jar with {betterdays} mods - versions {3.3.4.4}
-[24nov.2025 23:48:48.034] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\betterdays-895618\7013807_mapped_official_1.20.1\betterdays-895618-7013807_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
-[24nov.2025 23:48:48.034] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-291874\6398227_mapped_official_1.20.1\serene-seasons-291874-6398227_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.035] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-291874-6398227_mapped_official_1.20.1.jar with {sereneseasons} mods - versions {9.1.0.2}
-[24nov.2025 23:48:48.035] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-291874\6398227_mapped_official_1.20.1\serene-seasons-291874-6398227_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.035] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.035] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar with {crackerslib} mods - versions {1.20.1-0.4.6}
-[24nov.2025 23:48:48.035] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.035] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\glitchcore-955399\5787839_mapped_official_1.20.1\glitchcore-955399-5787839_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.036] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file glitchcore-955399-5787839_mapped_official_1.20.1.jar with {glitchcore} mods - versions {0.0.1.1}
-[24nov.2025 23:48:48.036] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\glitchcore-955399\5787839_mapped_official_1.20.1\glitchcore-955399-5787839_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.036] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\jade-324717\6271651_mapped_official_1.20.1\jade-324717-6271651_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.036] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jade-324717-6271651_mapped_official_1.20.1.jar with {jade} mods - versions {11.13.1+forge}
-[24nov.2025 23:48:48.036] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\jade-324717\6271651_mapped_official_1.20.1\jade-324717-6271651_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
-[24nov.2025 23:48:48.036] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\simpleclouds\0.7.3+1.20.1-forge_mapped_official_1.20.1\simpleclouds-0.7.3+1.20.1-forge_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.036] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file simpleclouds-0.7.3+1.20.1-forge_mapped_official_1.20.1.jar with {simpleclouds} mods - versions {0.7.3+1.20.1-forge}
-[24nov.2025 23:48:48.037] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\simpleclouds\0.7.3+1.20.1-forge_mapped_official_1.20.1\simpleclouds-0.7.3+1.20.1-forge_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.037] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
-[24nov.2025 23:48:48.037] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file whitenoise-1.20.1-forge-1.0.0.jar with {whitenoise} mods - versions {1.0.0}
-[24nov.2025 23:48:48.037] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
-[24nov.2025 23:48:48.037] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\architectury-api-419699\5137938_mapped_official_1.20.1\architectury-api-419699-5137938_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.037] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file architectury-api-419699-5137938_mapped_official_1.20.1.jar with {architectury} mods - versions {9.2.14}
-[24nov.2025 23:48:48.037] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\architectury-api-419699\5137938_mapped_official_1.20.1\architectury-api-419699-5137938_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
-[24nov.2025 23:48:48.037] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\auroras-1105290\6040671_mapped_official_1.20.1\auroras-1105290-6040671_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file auroras-1105290-6040671_mapped_official_1.20.1.jar with {auroras} mods - versions {1.6.2}
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\auroras-1105290\6040671_mapped_official_1.20.1\auroras-1105290-6040671_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[40,)]]
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\mezz\jei\jei-1.20.1-forge\15.8.2.26_mapped_official_1.20.1\jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar with {jei} mods - versions {15.8.2.26}
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\mezz\jei\jei-1.20.1-forge\15.8.2.26_mapped_official_1.20.1\jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\lithostitched-936015\6742615_mapped_official_1.20.1\lithostitched-936015-6742615_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file lithostitched-936015-6742615_mapped_official_1.20.1.jar with {lithostitched} mods - versions {1.4.11}
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\lithostitched-936015\6742615_mapped_official_1.20.1\lithostitched-936015-6742615_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\spark-361579\4738952_mapped_official_1.20.1\spark-361579-4738952_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file spark-361579-4738952_mapped_official_1.20.1.jar with {spark} mods - versions {1.10.53}
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\spark-361579\4738952_mapped_official_1.20.1\spark-361579-4738952_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[34,)]]
-[24nov.2025 23:48:48.038] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\cloth-config-348521\5729105_mapped_official_1.20.1\cloth-config-348521-5729105_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.040] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file cloth-config-348521-5729105_mapped_official_1.20.1.jar with {cloth_config} mods - versions {11.1.136}
-[24nov.2025 23:48:48.040] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\cloth-config-348521\5729105_mapped_official_1.20.1\cloth-config-348521-5729105_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[34,)]]
-[24nov.2025 23:48:48.040] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate /
-[24nov.2025 23:48:48.041] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file with {forge} mods - versions {47.4.9}
-[24nov.2025 23:48:48.041] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file / with languages [LanguageSpec[languageName=javafml, acceptedVersions=[24,]]]
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Found coremod field_to_method with Javascript path coremods/field_to_method.js
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Found coremod field_to_instanceof with Javascript path coremods/field_to_instanceof.js
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Found coremod add_bouncer_method with Javascript path coremods/add_bouncer_method.js
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Found coremod method_redirector with Javascript path coremods/method_redirector.js
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Found coremod coremods/field_to_method.js
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Found coremod coremods/field_to_instanceof.js
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Found coremod coremods/add_bouncer_method.js
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Found coremod coremods/method_redirector.js
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\rainboows-1121832\6421980_mapped_official_1.20.1\rainboows-1121832-6421980_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.044] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file rainboows-1121832-6421980_mapped_official_1.20.1.jar with {rainbows} mods - versions {1.5}
-[24nov.2025 23:48:48.045] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\rainboows-1121832\6421980_mapped_official_1.20.1\rainboows-1121832-6421980_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[40,)]]
-[24nov.2025 23:48:48.045] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\io.github.xgabou\gaboulibs_forge\1.2\7247c49d39f07ab149de67aa345e8cf65978be1c\gaboulibs_forge-1.2.jar
-[24nov.2025 23:48:48.045] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file gaboulibs_forge-1.2.jar with {gaboulibs} mods - versions {1.2}
-[24nov.2025 23:48:48.045] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\io.github.xgabou\gaboulibs_forge\1.2\7247c49d39f07ab149de67aa345e8cf65978be1c\gaboulibs_forge-1.2.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.045] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\chloride-931925\6615986_mapped_official_1.20.1\chloride-931925-6615986_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.046] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file chloride-931925-6615986_mapped_official_1.20.1.jar with {chloride} mods - versions {1.7.2}
-[24nov.2025 23:48:48.046] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\chloride-931925\6615986_mapped_official_1.20.1\chloride-931925-6615986_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,48)]]
-[24nov.2025 23:48:48.046] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\embeddium-908741\5681725_mapped_official_1.20.1\embeddium-908741-5681725_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.046] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file embeddium-908741-5681725_mapped_official_1.20.1.jar with {embeddium,rubidium} mods - versions {0.3.31+mc1.20.1,0.7.1}
-[24nov.2025 23:48:48.046] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\embeddium-908741\5681725_mapped_official_1.20.1\embeddium-908741-5681725_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.046] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\build\resources\main
-[24nov.2025 23:48:48.047] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file main with {projectatmosphere} mods - versions {0.6.0.0-pre2}
-[24nov.2025 23:48:48.047] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file G:\Project-Atmosphere\build\resources\main with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.047] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\libs\tectonic-3.0.17-forge-1.20.1-dev.jar
-[24nov.2025 23:48:48.047] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-3.0.17-forge-1.20.1-dev.jar with {tectonic} mods - versions {3.0.17}
-[24nov.2025 23:48:48.047] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file G:\Project-Atmosphere\libs\tectonic-3.0.17-forge-1.20.1-dev.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.047] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-plus-1288843\7174860_mapped_official_1.20.1\serene-seasons-plus-1288843-7174860_mapped_official_1.20.1.jar
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-plus-1288843-7174860_mapped_official_1.20.1.jar with {sereneseasonsplus} mods - versions {4.1.0}
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-plus-1288843\7174860_mapped_official_1.20.1\serene-seasons-plus-1288843-7174860_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file mixinextras-forge-0.3.5.jar with {mixinextras} mods - versions {0.3.5}
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages [LanguageSpec[languageName=javafml, acceptedVersions=*]]
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages []
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\simplecloudsapi-forge\1.4-1.20.1_mapped_official_1.20.1\simplecloudsapi-forge-1.4-1.20.1_mapped_official_1.20.1.jar with languages []
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file mixinextras-forge-0.3.5.jar with {mixinextras} mods - versions {0.3.5}
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages [LanguageSpec[languageName=javafml, acceptedVersions=*]]
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages []
-[24nov.2025 23:48:48.048] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\simplecloudsapi-forge\1.4-1.20.1_mapped_official_1.20.1\simplecloudsapi-forge-1.4-1.20.1_mapped_official_1.20.1.jar with languages []
-[24nov.2025 23:48:48.050] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: End scan trigger - transformation service fml
-[24nov.2025 23:48:48.057] [main/DEBUG] [net.minecraftforge.fml.loading.LanguageLoadingProvider/CORE]: Found 3 language providers
-[24nov.2025 23:48:48.057] [main/DEBUG] [net.minecraftforge.fml.loading.LanguageLoadingProvider/CORE]: Found language provider minecraft, version 1.0
-[24nov.2025 23:48:48.058] [main/DEBUG] [net.minecraftforge.fml.loading.LanguageLoadingProvider/CORE]: Found language provider lowcodefml, version 47
-[24nov.2025 23:48:48.058] [main/DEBUG] [net.minecraftforge.fml.loading.LanguageLoadingProvider/CORE]: Found language provider javafml, version 47
-[24nov.2025 23:48:48.063] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid simplecloudsapi.forge, selecting most recent based on version data
-[24nov.2025 23:48:48.064] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file simplecloudsapi-forge-1.4-1.20.1_mapped_official_1.20.1.jar for modid simplecloudsapi.forge with version 1.4-1.20.1
-[24nov.2025 23:48:48.064] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid mixinextras, selecting most recent based on version data
-[24nov.2025 23:48:48.064] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file mixinextras-forge-0.3.5.jar for modid mixinextras with version 0.3.5
-[24nov.2025 23:48:48.064] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid MixinExtras, selecting most recent based on version data
-[24nov.2025 23:48:48.064] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file MixinExtras-0.3.5.jar for modid MixinExtras with version 0.0NONE
-[24nov.2025 23:48:48.066] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/]: Configured system mods: [minecraft, forge]
-[24nov.2025 23:48:48.066] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/]: Found system mod: minecraft
-[24nov.2025 23:48:48.066] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/]: Found system mod: forge
-[24nov.2025 23:48:48.070] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/LOADING]: Found 47 mod requirements (40 mandatory, 7 optional)
-[24nov.2025 23:48:48.071] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/LOADING]: Found 0 mod requirements missing (0 mandatory, 0 optional)
-[24nov.2025 23:48:48.446] [main/DEBUG] [net.minecraftforge.fml.loading.MCPNamingService/CORE]: Loaded 33222 method mappings from methods.csv
-[24nov.2025 23:48:48.468] [main/DEBUG] [net.minecraftforge.fml.loading.MCPNamingService/CORE]: Loaded 31003 field mappings from fields.csv
-[24nov.2025 23:48:48.518] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Transformation services loading transformers
-[24nov.2025 23:48:48.518] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initializing transformers for transformation service mixin
-[24nov.2025 23:48:48.519] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initialized transformers for transformation service mixin
-[24nov.2025 23:48:48.519] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initializing transformers for transformation service fml
-[24nov.2025 23:48:48.519] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Loading coremod transformers
-[24nov.2025 23:48:48.519] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: Loading CoreMod from coremods/field_to_method.js
-[24nov.2025 23:48:48.764] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: CoreMod loaded successfully
-[24nov.2025 23:48:48.764] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: Loading CoreMod from coremods/field_to_instanceof.js
-[24nov.2025 23:48:48.854] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: CoreMod loaded successfully
-[24nov.2025 23:48:48.854] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: Loading CoreMod from coremods/add_bouncer_method.js
-[24nov.2025 23:48:48.885] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: CoreMod loaded successfully
-[24nov.2025 23:48:48.885] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: Loading CoreMod from coremods/method_redirector.js
-[24nov.2025 23:48:48.936] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: CoreMod loaded successfully
-[24nov.2025 23:48:48.952] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@6198e9b5 to Target : CLASS {Lnet/minecraft/world/level/biome/Biome;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@d535a3d to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/Structure;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@2d760326 to Target : CLASS {Lnet/minecraft/world/effect/MobEffectInstance;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@9e54c59 to Target : CLASS {Lnet/minecraft/world/level/block/LiquidBlock;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@5dbb50f3 to Target : CLASS {Lnet/minecraft/world/item/BucketItem;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a2e7bcb to Target : CLASS {Lnet/minecraft/world/level/block/StairBlock;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@575c3e9b to Target : CLASS {Lnet/minecraft/world/level/block/FlowerPotBlock;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@74f827ad to Target : CLASS {Lnet/minecraft/world/item/ItemStack;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@73c3cd09 to Target : CLASS {Lnet/minecraft/network/play/client/CClientSettingsPacket;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/EntityType;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces$OceanRuinPiece;} {} {V}
-[24nov.2025 23:48:48.954] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/npc/CatSpawner;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/server/commands/RaidCommand;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/structures/SwampHutPiece;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/level/levelgen/PatrolSpawner;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/monster/Spider;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/server/commands/SummonCommand;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/level/NaturalSpawner;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces$OceanMonumentPiece;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/monster/Zombie;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/npc/Villager;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/animal/frog/Tadpole;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/raid/Raid;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces$WoodlandMansionPiece;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/animal/horse/SkeletonTrapGoal;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/monster/Strider;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/monster/ZombieVillager;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/level/levelgen/PhantomSpawner;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/monster/Evoker$EvokerSummonSpellGoal;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@4a8a0099 to Target : CLASS {Lnet/minecraft/world/entity/ai/village/VillageSiege;} {} {V}
-[24nov.2025 23:48:48.955] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initialized transformers for transformation service fml
-[24nov.2025 23:48:49.287] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:ModLauncher Root Container(ModLauncher:4f56a0a2)]
-[24nov.2025 23:48:49.287] [main/DEBUG] [mixin/]: Registering mixin config: projectatmosphere.mixins.json
-[24nov.2025 23:48:49.310] [main/DEBUG] [mixin/]: Compatibility level JAVA_17 specified by projectatmosphere.mixins.json is higher than the maximum level supported by this version of mixin (JAVA_13).
-[24nov.2025 23:48:49.314] [main/INFO] [mixin/]: Compatibility level set to JAVA_17
-[24nov.2025 23:48:49.314] [main/ERROR] [mixin/]: Mixin config projectatmosphere.mixins.json does not specify "minVersion" property
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Processing launch tasks for PlatformAgent[MixinPlatformAgentDefault:ModLauncher Root Container(ModLauncher:4f56a0a2)]
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(betterdays)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(betterdays)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(betterdays)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(betterdays)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(betterdays)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(betterdays)]
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(minecraft)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(minecraft)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(minecraft)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(minecraft)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(minecraft)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(minecraft)]
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(jade)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(jade)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(jade)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(jade)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(jade)
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(jade)]
-[24nov.2025 23:48:49.315] [main/DEBUG] [mixin/]: Registering mixin config: jade.mixins.json
-[24nov.2025 23:48:49.316] [main/DEBUG] [mixin/]: Compatibility level JAVA_16 specified by jade.mixins.json is higher than the maximum level supported by this version of mixin (JAVA_13).
-[24nov.2025 23:48:49.316] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(jei)
-[24nov.2025 23:48:49.316] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(jei)
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(jei)
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(jei)
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(jei)
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(jei)]
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(lithostitched)
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(lithostitched)
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(lithostitched)
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(lithostitched)
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(lithostitched)
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(lithostitched)]
-[24nov.2025 23:48:49.317] [main/DEBUG] [mixin/]: Registering mixin config: lithostitched.mixins.json
-[24nov.2025 23:48:49.318] [main/DEBUG] [mixin/]: Registering mixin config: lithostitched.forge.mixins.json
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(spark)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(spark)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(spark)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(spark)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(spark)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(spark)]
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(sereneseasonsplus)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(sereneseasonsplus)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(sereneseasonsplus)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(sereneseasonsplus)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(sereneseasonsplus)
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(sereneseasonsplus)]
-[24nov.2025 23:48:49.320] [main/DEBUG] [mixin/]: Registering mixin config: sereneseasonsplus.mixins.json
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(crackerslib)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(crackerslib)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(crackerslib)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(crackerslib)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(crackerslib)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(crackerslib)]
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Registering mixin config: crackerslib.mixins.json
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(mixinextras)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(mixinextras)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(mixinextras)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(mixinextras)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(mixinextras)
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(mixinextras)]
-[24nov.2025 23:48:49.321] [main/DEBUG] [mixin/]: Registering mixin config: mixinextras.init.mixins.json
-[24nov.2025 23:48:49.322] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(glitchcore)
-[24nov.2025 23:48:49.322] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(glitchcore)
-[24nov.2025 23:48:49.322] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(glitchcore)
-[24nov.2025 23:48:49.322] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(glitchcore)
-[24nov.2025 23:48:49.322] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(glitchcore)
-[24nov.2025 23:48:49.322] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(glitchcore)]
-[24nov.2025 23:48:49.322] [main/DEBUG] [mixin/]: Registering mixin config: glitchcore.mixins.json
-[24nov.2025 23:48:49.322] [main/DEBUG] [mixin/]: Registering mixin config: glitchcore.forge.mixins.json
-[24nov.2025 23:48:49.322] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(sereneseasons)
-[24nov.2025 23:48:49.323] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(sereneseasons)
-[24nov.2025 23:48:49.323] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(sereneseasons)
-[24nov.2025 23:48:49.323] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(sereneseasons)
-[24nov.2025 23:48:49.323] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(sereneseasons)
-[24nov.2025 23:48:49.323] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(sereneseasons)]
-[24nov.2025 23:48:49.323] [main/DEBUG] [mixin/]: Registering mixin config: sereneseasons.mixins.json
-[24nov.2025 23:48:49.323] [main/DEBUG] [mixin/]: Registering mixin config: sereneseasons.forge.mixins.json
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(simpleclouds)
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(simpleclouds)
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(simpleclouds)
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(simpleclouds)
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(simpleclouds)
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(simpleclouds)]
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: Registering mixin config: simpleclouds.mixins.json
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(projectatmosphere)
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(projectatmosphere)
-[24nov.2025 23:48:49.324] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(projectatmosphere)
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(projectatmosphere)
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(projectatmosphere)
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(projectatmosphere)]
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(architectury)
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(architectury)
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(architectury)
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(architectury)
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(architectury)
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(architectury)]
-[24nov.2025 23:48:49.325] [main/DEBUG] [mixin/]: Registering mixin config: architectury.mixins.json
-[24nov.2025 23:48:49.326] [main/DEBUG] [mixin/]: Compatibility level JAVA_16 specified by architectury.mixins.json is higher than the maximum level supported by this version of mixin (JAVA_13).
-[24nov.2025 23:48:49.327] [main/DEBUG] [mixin/]: Registering mixin config: architectury-common.mixins.json
-[24nov.2025 23:48:49.327] [main/DEBUG] [mixin/]: Compatibility level JAVA_16 specified by architectury-common.mixins.json is higher than the maximum level supported by this version of mixin (JAVA_13).
-[24nov.2025 23:48:49.328] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(whitenoise)
-[24nov.2025 23:48:49.328] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(whitenoise)
-[24nov.2025 23:48:49.328] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(whitenoise)
-[24nov.2025 23:48:49.328] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(whitenoise)
-[24nov.2025 23:48:49.328] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(whitenoise)
-[24nov.2025 23:48:49.328] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(whitenoise)]
-[24nov.2025 23:48:49.328] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(cloth_config)
-[24nov.2025 23:48:49.328] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(cloth_config)
-[24nov.2025 23:48:49.328] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(cloth_config)
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(cloth_config)
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(cloth_config)
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(cloth_config)]
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(gaboulibs)
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(gaboulibs)
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(gaboulibs)
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(gaboulibs)
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(gaboulibs)
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(gaboulibs)]
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: Registering mixin config: gaboulibs.mixins.json
-[24nov.2025 23:48:49.329] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(forge)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(forge)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(forge)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(forge)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(forge)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(forge)]
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(auroras)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(auroras)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(auroras)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(auroras)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(auroras)
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(auroras)]
-[24nov.2025 23:48:49.330] [main/DEBUG] [mixin/]: Registering mixin config: auroras.mixins.json
-[24nov.2025 23:48:49.331] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(rainbows)
-[24nov.2025 23:48:49.331] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(rainbows)
-[24nov.2025 23:48:49.331] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(rainbows)
-[24nov.2025 23:48:49.331] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(rainbows)
-[24nov.2025 23:48:49.331] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(rainbows)
-[24nov.2025 23:48:49.331] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(rainbows)]
-[24nov.2025 23:48:49.331] [main/DEBUG] [mixin/]: Registering mixin config: rainbows.mixins.json
-[24nov.2025 23:48:49.331] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(embeddium)
-[24nov.2025 23:48:49.331] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(embeddium)
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(embeddium)
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(embeddium)
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(embeddium)
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(embeddium)]
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: Registering mixin config: embeddium.mixins.json
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(chloride)
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(chloride)
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(chloride)
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(chloride)
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(chloride)
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(chloride)]
-[24nov.2025 23:48:49.332] [main/DEBUG] [mixin/]: Registering mixin config: chloride.mixin.json
-[24nov.2025 23:48:49.333] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(tectonic)
-[24nov.2025 23:48:49.333] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(tectonic)
-[24nov.2025 23:48:49.333] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(tectonic)
-[24nov.2025 23:48:49.333] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(tectonic)
-[24nov.2025 23:48:49.333] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(tectonic)
-[24nov.2025 23:48:49.333] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(tectonic)]
-[24nov.2025 23:48:49.333] [main/DEBUG] [mixin/]: Registering mixin config: tectonic.mixins.json
-[24nov.2025 23:48:49.333] [main/DEBUG] [mixin/]: Registering mixin config: tectonic_1.20.1.mixins.json
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(MixinExtras)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(MixinExtras)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(MixinExtras)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(MixinExtras)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(MixinExtras)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(MixinExtras)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(simplecloudsapi.forge)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(simplecloudsapi.forge)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(simplecloudsapi.forge)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(simplecloudsapi.forge)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(simplecloudsapi.forge)
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(simplecloudsapi.forge)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: inject() running with 26 agents
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:ModLauncher Root Container(ModLauncher:4f56a0a2)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(betterdays)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(minecraft)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(jade)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(jei)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(lithostitched)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(spark)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(sereneseasonsplus)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(crackerslib)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(mixinextras)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(glitchcore)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(sereneseasons)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(simpleclouds)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(projectatmosphere)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(architectury)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(whitenoise)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(cloth_config)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(gaboulibs)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(forge)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(auroras)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(rainbows)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(embeddium)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(chloride)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(tectonic)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(MixinExtras)]
-[24nov.2025 23:48:49.335] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(simplecloudsapi.forge)]
-[24nov.2025 23:48:49.336] [main/INFO] [cpw.mods.modlauncher.LaunchServiceHandler/MODLAUNCHER]: Launching target 'forgedatauserdev' with arguments [--gameDir, ., --assetsDir, C:\Users\matga\.gradle\caches\forge_gradle\assets, --assetIndex, 5, --mod, projectatmosphere, --all, --output, G:\Project-Atmosphere\src\generated\resources, --existing, G:\Project-Atmosphere\src\main\resources]
-[24nov.2025 23:48:49.419] [main/DEBUG] [mixin/]: Error cleaning class output directory: .mixin.out
-[24nov.2025 23:48:49.421] [main/DEBUG] [mixin/]: Preparing mixins for MixinEnvironment[DEFAULT]
-[24nov.2025 23:48:49.421] [main/DEBUG] [mixin/]: Selecting config projectatmosphere.mixins.json
-[24nov.2025 23:48:49.453] [main/WARN] [mixin/]: Reference map 'projectatmosphere.refmap.json' for projectatmosphere.mixins.json could not be read. If this is a development environment you can ignore this message
-[24nov.2025 23:48:49.454] [main/DEBUG] [mixin/]: Selecting config jade.mixins.json
-[24nov.2025 23:48:49.600] [main/INFO] [mixin/]: Remapping refMap jade.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.600] [main/DEBUG] [mixin/]: Selecting config lithostitched.mixins.json
-[24nov.2025 23:48:49.601] [main/INFO] [mixin/]: Remapping refMap lithostitched.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.601] [main/DEBUG] [mixin/]: Selecting config lithostitched.forge.mixins.json
-[24nov.2025 23:48:49.602] [main/INFO] [mixin/]: Remapping refMap lithostitched.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.602] [main/DEBUG] [mixin/]: Selecting config sereneseasonsplus.mixins.json
-[24nov.2025 23:48:49.602] [main/INFO] [mixin/]: Remapping refMap sereneseasonsplus-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.602] [main/DEBUG] [mixin/]: Selecting config crackerslib.mixins.json
-[24nov.2025 23:48:49.602] [main/INFO] [mixin/]: Remapping refMap crackerslib.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.602] [main/DEBUG] [mixin/]: Selecting config mixinextras.init.mixins.json
-[24nov.2025 23:48:49.640] [main/DEBUG] [MixinExtras|Service/]: com.llamalad7.mixinextras.service.MixinExtrasServiceImpl(version=0.3.5) is taking over from null
-[24nov.2025 23:48:49.690] [main/DEBUG] [mixin/]: Registering new injector for @Inject with org.spongepowered.asm.mixin.injection.struct.CallbackInjectionInfo
-[24nov.2025 23:48:49.692] [main/DEBUG] [mixin/]: Registering new injector for @ModifyArg with org.spongepowered.asm.mixin.injection.struct.ModifyArgInjectionInfo
-[24nov.2025 23:48:49.692] [main/DEBUG] [mixin/]: Registering new injector for @ModifyArgs with org.spongepowered.asm.mixin.injection.struct.ModifyArgsInjectionInfo
-[24nov.2025 23:48:49.694] [main/DEBUG] [mixin/]: Registering new injector for @Redirect with org.spongepowered.asm.mixin.injection.struct.RedirectInjectionInfo
-[24nov.2025 23:48:49.695] [main/DEBUG] [mixin/]: Registering new injector for @ModifyVariable with org.spongepowered.asm.mixin.injection.struct.ModifyVariableInjectionInfo
-[24nov.2025 23:48:49.696] [main/DEBUG] [mixin/]: Registering new injector for @ModifyConstant with org.spongepowered.asm.mixin.injection.struct.ModifyConstantInjectionInfo
-[24nov.2025 23:48:49.704] [main/DEBUG] [mixin/]: Selecting config glitchcore.mixins.json
-[24nov.2025 23:48:49.705] [main/INFO] [mixin/]: Remapping refMap glitchcore.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.705] [main/DEBUG] [mixin/]: Selecting config glitchcore.forge.mixins.json
-[24nov.2025 23:48:49.705] [main/INFO] [mixin/]: Remapping refMap glitchcore.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.705] [main/DEBUG] [mixin/]: Selecting config sereneseasons.mixins.json
-[24nov.2025 23:48:49.706] [main/INFO] [mixin/]: Remapping refMap sereneseasons.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.706] [main/DEBUG] [mixin/]: Selecting config sereneseasons.forge.mixins.json
-[24nov.2025 23:48:49.706] [main/INFO] [mixin/]: Remapping refMap sereneseasons.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.706] [main/DEBUG] [mixin/]: Selecting config simpleclouds.mixins.json
-[24nov.2025 23:48:49.707] [main/INFO] [mixin/]: Remapping refMap simpleclouds.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.707] [main/DEBUG] [mixin/]: Selecting config architectury.mixins.json
-[24nov.2025 23:48:49.710] [main/INFO] [mixin/]: Remapping refMap architectury-forge-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.710] [main/DEBUG] [mixin/]: Selecting config architectury-common.mixins.json
-[24nov.2025 23:48:49.710] [main/INFO] [mixin/]: Remapping refMap architectury-common-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.710] [main/DEBUG] [mixin/]: Selecting config gaboulibs.mixins.json
-[24nov.2025 23:48:49.711] [main/DEBUG] [mixin/]: Selecting config auroras.mixins.json
-[24nov.2025 23:48:49.711] [main/INFO] [mixin/]: Remapping refMap auroras.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.711] [main/DEBUG] [mixin/]: Selecting config rainbows.mixins.json
-[24nov.2025 23:48:49.712] [main/WARN] [mixin/]: Reference map 'rainbows.refmap.json' for rainbows.mixins.json could not be read. If this is a development environment you can ignore this message
-[24nov.2025 23:48:49.712] [main/DEBUG] [mixin/]: Selecting config embeddium.mixins.json
-[24nov.2025 23:48:49.720] [main/INFO] [Embeddium/]: Loaded configuration file for Embeddium: 68 options available, 0 override(s) found
-[24nov.2025 23:48:49.722] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Searching for graphics cards...
-[24nov.2025 23:48:49.839] [main/DEBUG] [oshi.util.FileUtil/]: No oshi.properties file found from ClassLoader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1
-[24nov.2025 23:48:49.963] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Found graphics card: GraphicsAdapterInfo[vendor=UNKNOWN, name=Meta Virtual Monitor, version=DriverVersion=17.12.55.198]
-[24nov.2025 23:48:49.963] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Found graphics card: GraphicsAdapterInfo[vendor=NVIDIA, name=NVIDIA GeForce RTX 4070 Ti SUPER, version=DriverVersion=32.0.15.8180]
-[24nov.2025 23:48:49.967] [main/WARN] [Embeddium-Workarounds/]: Embeddium has applied one or more workarounds to prevent crashes or other issues on your system: [NVIDIA_THREADED_OPTIMIZATIONS]
-[24nov.2025 23:48:49.967] [main/WARN] [Embeddium-Workarounds/]: This is not necessarily an issue, but it may result in certain features or optimizations being disabled. You can sometimes fix these issues by upgrading your graphics driver.
-[24nov.2025 23:48:49.969] [main/INFO] [mixin/]: Remapping refMap embeddium-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.969] [main/DEBUG] [mixin/]: Selecting config chloride.mixin.json
-[24nov.2025 23:48:49.971] [main/INFO] [mixin/]: Remapping refMap chloride.mixin-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.971] [main/DEBUG] [mixin/]: Selecting config tectonic.mixins.json
-[24nov.2025 23:48:49.971] [main/DEBUG] [mixin/]: Selecting config tectonic_1.20.1.mixins.json
-[24nov.2025 23:48:49.972] [main/DEBUG] [mixin/]: Preparing projectatmosphere.mixins.json (12)
-[24nov.2025 23:48:50.018] [main/DEBUG] [mixin/]: Preparing jade.mixins.json (2)
-[24nov.2025 23:48:50.022] [main/DEBUG] [mixin/]: Preparing lithostitched.mixins.json (18)
-[24nov.2025 23:48:50.044] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate
-[24nov.2025 23:48:50.192] [main/DEBUG] [mixin/]: Preparing lithostitched.forge.mixins.json (2)
-[24nov.2025 23:48:50.193] [main/DEBUG] [mixin/]: Preparing sereneseasonsplus.mixins.json (7)
-[24nov.2025 23:48:50.200] [main/DEBUG] [mixin/]: Preparing crackerslib.mixins.json (2)
-[24nov.2025 23:48:50.203] [main/DEBUG] [mixin/]: Preparing mixinextras.init.mixins.json (0)
-[24nov.2025 23:48:50.203] [main/DEBUG] [mixin/]: Preparing glitchcore.mixins.json (4)
-[24nov.2025 23:48:50.204] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/item/ItemStack
-[24nov.2025 23:48:50.222] [main/DEBUG] [mixin/]: Preparing glitchcore.forge.mixins.json (7)
-[24nov.2025 23:48:50.229] [main/DEBUG] [mixin/]: Preparing sereneseasons.mixins.json (6)
-[24nov.2025 23:48:50.230] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/biome/Biome
-[24nov.2025 23:48:50.245] [main/DEBUG] [mixin/]: Preparing sereneseasons.forge.mixins.json (0)
-[24nov.2025 23:48:50.245] [main/DEBUG] [mixin/]: Preparing simpleclouds.mixins.json (20)
-[24nov.2025 23:48:50.267] [main/WARN] [mixin/]: Error loading class: org/vivecraft/client_vr/provider/VRRenderer (java.lang.ClassNotFoundException: org.vivecraft.client_vr.provider.VRRenderer)
-[24nov.2025 23:48:50.267] [main/DEBUG] [mixin/]: Skipping virtual target org.vivecraft.client_vr.provider.VRRenderer for simpleclouds.mixins.json:vivecraft.MixinVRRenderer
-[24nov.2025 23:48:50.267] [main/DEBUG] [mixin/]: Preparing architectury.mixins.json (9)
-[24nov.2025 23:48:50.272] [main/DEBUG] [mixin/]: Preparing architectury-common.mixins.json (10)
-[24nov.2025 23:48:50.276] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/item/BucketItem
-[24nov.2025 23:48:50.279] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/EntityType
-[24nov.2025 23:48:50.296] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/block/LiquidBlock
-[24nov.2025 23:48:50.299] [main/DEBUG] [mixin/]: Preparing gaboulibs.mixins.json (0)
-[24nov.2025 23:48:50.300] [main/DEBUG] [mixin/]: Preparing auroras.mixins.json (3)
-[24nov.2025 23:48:50.301] [main/WARN] [mixin/]: Error loading class: com/aetherteam/aether/client/renderer/level/AetherSkyRenderEffects (java.lang.ClassNotFoundException: com.aetherteam.aether.client.renderer.level.AetherSkyRenderEffects)
-[24nov.2025 23:48:50.301] [main/WARN] [mixin/]: @Mixin target com.aetherteam.aether.client.renderer.level.AetherSkyRenderEffects was not found auroras.mixins.json:client.AetherSkyRenderEffectsMixin
-[24nov.2025 23:48:50.301] [main/DEBUG] [mixin/]: Preparing rainbows.mixins.json (0)
-[24nov.2025 23:48:50.301] [main/DEBUG] [mixin/]: Preparing embeddium.mixins.json (0)
-[24nov.2025 23:48:50.301] [main/DEBUG] [mixin/]: Preparing chloride.mixin.json (32)
-[24nov.2025 23:48:50.315] [main/WARN] [mixin/]: Error loading class: dev/emi/emi/screen/EmiScreenManager (java.lang.ClassNotFoundException: dev.emi.emi.screen.EmiScreenManager)
-[24nov.2025 23:48:50.315] [main/DEBUG] [mixin/]: Skipping virtual target dev.emi.emi.screen.EmiScreenManager for chloride.mixin.json:jei_rei_emi.EmiOverlayMixin
-[24nov.2025 23:48:50.317] [main/WARN] [mixin/]: Error loading class: me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl (java.lang.ClassNotFoundException: me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl)
-[24nov.2025 23:48:50.317] [main/DEBUG] [mixin/]: Skipping virtual target me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl for chloride.mixin.json:jei_rei_emi.ReiOverlayMixin
-[24nov.2025 23:48:50.317] [main/DEBUG] [mixin/]: Preparing tectonic.mixins.json (13)
-[24nov.2025 23:48:50.325] [main/DEBUG] [mixin/]: Preparing tectonic_1.20.1.mixins.json (3)
-[24nov.2025 23:48:50.347] [main/DEBUG] [mixin/]: Inner class glitchcore/forge/mixin/impl/MixinPacketHandler$1 in glitchcore/forge/mixin/impl/MixinPacketHandler on glitchcore/network/PacketHandler gets unique name glitchcore/network/PacketHandler$Anonymous$fe06fa1677ef4bf39dbb3409f7251026
-[24nov.2025 23:48:50.436] [main/DEBUG] [mixin/]: Prepared 143 mixins in 1,014 sec (7,1ms avg) (0ms load, 0ms transform, 0ms plugin)
-[24nov.2025 23:48:50.512] [main/DEBUG] [io.netty.util.internal.logging.InternalLoggerFactory/]: Using SLF4J as the default logging framework
-[24nov.2025 23:48:50.521] [main/DEBUG] [io.netty.util.ResourceLeakDetector/]: -Dio.netty.leakDetection.level: simple
-[24nov.2025 23:48:50.521] [main/DEBUG] [io.netty.util.ResourceLeakDetector/]: -Dio.netty.leakDetection.targetRecords: 4
-[24nov.2025 23:48:50.741] [main/INFO] [net.minecraftforge.data.loading.DatagenModLoader/]: Initializing Data Gatherer for mods [projectatmosphere]
-[24nov.2025 23:48:50.752] [main/INFO] [MixinExtras|Service/]: Initializing MixinExtras via com.llamalad7.mixinextras.service.MixinExtrasServiceImpl(version=0.3.5).
-[24nov.2025 23:48:50.753] [main/DEBUG] [mixin/]: Registering new injector for @SugarWrapper with com.llamalad7.mixinextras.sugar.impl.SugarWrapperInjectionInfo
-[24nov.2025 23:48:50.754] [main/DEBUG] [mixin/]: Registering new injector for @FactoryRedirectWrapper with com.llamalad7.mixinextras.wrapper.factory.FactoryRedirectWrapperInjectionInfo
-[24nov.2025 23:48:50.757] [main/DEBUG] [mixin/]: Mixing common.MappedRegistryAccessor from lithostitched.mixins.json into net.minecraft.core.MappedRegistry
-[24nov.2025 23:48:50.834] [main/DEBUG] [mixin/]: Mixing inject.MixinGameEvent from architectury-common.mixins.json into net.minecraft.world.level.gameevent.GameEvent
-[24nov.2025 23:48:50.842] [main/DEBUG] [mixin/]: Mixing common.HolderReferenceAccessor from lithostitched.mixins.json into net.minecraft.core.Holder$Reference
-[24nov.2025 23:48:50.868] [main/DEBUG] [mixin/]: Mixing inject.MixinBlock from architectury-common.mixins.json into net.minecraft.world.level.block.Block
-[24nov.2025 23:48:50.887] [main/DEBUG] [mixin/]: Mixing MixinBlockStateBase from sereneseasons.mixins.json into net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase
-[24nov.2025 23:48:50.926] [main/DEBUG] [mixin/]: Mixing MixinLevel from sereneseasons.mixins.json into net.minecraft.world.level.Level
-[24nov.2025 23:48:50.928] [main/DEBUG] [mixin/]: Mixing MixinLevel from simpleclouds.mixins.json into net.minecraft.world.level.Level
-[24nov.2025 23:48:50.945] [main/DEBUG] [mixin/]: Mixing ServerLevelSnowStormMixin from projectatmosphere.mixins.json into net.minecraft.server.level.ServerLevel
-[24nov.2025 23:48:50.947] [main/DEBUG] [mixin/]: Mixing ServerLevelMixin from sereneseasonsplus.mixins.json into net.minecraft.server.level.ServerLevel
-[24nov.2025 23:48:50.970] [main/DEBUG] [mixin/]: Mixing MixinServerLevel from glitchcore.mixins.json into net.minecraft.server.level.ServerLevel
-[24nov.2025 23:48:50.971] [main/DEBUG] [mixin/]: Mixing MixinServerLevel from sereneseasons.mixins.json into net.minecraft.server.level.ServerLevel
-[24nov.2025 23:48:50.971] [main/DEBUG] [mixin/]: Mixing MixinServerLevel from simpleclouds.mixins.json into net.minecraft.server.level.ServerLevel
-[24nov.2025 23:48:50.971] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$createCloudManager_init$1()Ldev/nonamecrackers2/simpleclouds/common/world/CloudData; to mdd47aa8$lambda$simpleclouds$createCloudManager_init$1$0 in simpleclouds.mixins.json:MixinServerLevel
-[24nov.2025 23:48:50.972] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$createCloudManager_init$0(Lnet/minecraft/nbt/CompoundTag;)Ldev/nonamecrackers2/simpleclouds/common/world/CloudData; to mdd47aa8$lambda$simpleclouds$createCloudManager_init$0$1 in simpleclouds.mixins.json:MixinServerLevel
-[24nov.2025 23:48:50.975] [main/DEBUG] [mixin/]: Mixing MixinServerLevelAccessor from simpleclouds.mixins.json into net.minecraft.server.level.ServerLevel
-[24nov.2025 23:48:51.085] [main/DEBUG] [mixin/]: Mixing inject.MixinFluid from architectury-common.mixins.json into net.minecraft.world.level.material.Fluid
-[24nov.2025 23:48:51.093] [main/DEBUG] [mixin/]: Mixing inject.MixinItem from architectury-common.mixins.json into net.minecraft.world.item.Item
-[24nov.2025 23:48:51.112] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/EntityType
-[24nov.2025 23:48:51.121] [main/DEBUG] [mixin/]: Mixing inject.MixinEntityType from architectury-common.mixins.json into net.minecraft.world.entity.EntityType
-[24nov.2025 23:48:51.122] [main/DEBUG] [mixin/]: Mixing EntityDistanceCullingMixin$EntityTypeMixin from chloride.mixin.json into net.minecraft.world.entity.EntityType
-[24nov.2025 23:48:51.152] [main/DEBUG] [mixin/]: Mixing ParticlesMixins$ParticleTypeMixin from chloride.mixin.json into net.minecraft.core.particles.ParticleType
-[24nov.2025 23:48:51.155] [main/DEBUG] [mixin/]: Mixing MixinBlockEntityType from crackerslib.mixins.json into net.minecraft.world.level.block.entity.BlockEntityType
-[24nov.2025 23:48:51.157] [main/DEBUG] [mixin/]: Mixing EntityDistanceCullingMixin$TileEntityTypeMixin from chloride.mixin.json into net.minecraft.world.level.block.entity.BlockEntityType
-[24nov.2025 23:48:51.205] [main/DEBUG] [mixin/]: Mixing WorldCarverMixin from tectonic.mixins.json into net.minecraft.world.level.levelgen.carver.WorldCarver
-[24nov.2025 23:48:51.282] [main/DEBUG] [mixin/]: Mixing features.render.immediate.DirectionMixin from embeddium.mixins.json into net.minecraft.core.Direction
-[24nov.2025 23:48:51.400] [main/DEBUG] [mixin/]: Mixing SpreadingSnowyDirtBlockMixin from sereneseasonsplus.mixins.json into net.minecraft.world.level.block.SpreadingSnowyDirtBlock
-[24nov.2025 23:48:51.413] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/block/LiquidBlock
-[24nov.2025 23:48:51.414] [main/DEBUG] [mixin/]: Mixing inject.MixinLiquidBlock from architectury-common.mixins.json into net.minecraft.world.level.block.LiquidBlock
-[24nov.2025 23:48:51.418] [main/DEBUG] [mixin/]: Mixing leaves_culling.LeavesBlockMixin from chloride.mixin.json into net.minecraft.world.level.block.LeavesBlock
-[24nov.2025 23:48:51.418] [main/DEBUG] [mixin/]: Mixing features.options.render_layers.LeavesBlockMixin from embeddium.mixins.json into net.minecraft.world.level.block.LeavesBlock
-[24nov.2025 23:48:51.426] [main/WARN] [mixin/]: Method overwrite conflict for skipRendering in embeddium.mixins.json:features.options.render_layers.LeavesBlockMixin, previously written by me.srrapero720.chloride.mixins.impl.leaves_culling.LeavesBlockMixin. Skipping method.
-[24nov.2025 23:48:51.426] [main/DEBUG] [mixin/]: Unexpected: Registered method skipRendering(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;)Z in net.minecraft.world.level.block.LeavesBlock was not merged
-[24nov.2025 23:48:51.432] [main/DEBUG] [mixin/]: Mixing FastBlocksMixins$BedMixin from chloride.mixin.json into net.minecraft.world.level.block.BedBlock
-[24nov.2025 23:48:51.448] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/block/StairBlock
-[24nov.2025 23:48:51.452] [main/DEBUG] [mixin/]: Mixing FastBlocksMixins$ChestsMixin from chloride.mixin.json into net.minecraft.world.level.block.ChestBlock
-[24nov.2025 23:48:51.509] [main/DEBUG] [mixin/]: Mixing FastBlocksMixins$ChestsMixin from chloride.mixin.json into net.minecraft.world.level.block.EnderChestBlock
-[24nov.2025 23:48:51.514] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/block/FlowerPotBlock
-[24nov.2025 23:48:51.623] [main/DEBUG] [mixin/]: Mixing MixinFallingBlockEntity from architectury.mixins.json into net.minecraft.world.entity.item.FallingBlockEntity
-[24nov.2025 23:48:51.991] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/item/ItemStack
-[24nov.2025 23:48:51.991] [main/DEBUG] [mixin/]: Mixing MixinItemStack from glitchcore.mixins.json into net.minecraft.world.item.ItemStack
-[24nov.2025 23:48:52.283] [main/DEBUG] [mixin/]: Mixing MixinLightningBolt from architectury-common.mixins.json into net.minecraft.world.entity.LightningBolt
-[24nov.2025 23:48:52.323] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/animal/frog/Tadpole
-[24nov.2025 23:48:52.370] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/item/BucketItem
-[24nov.2025 23:48:52.370] [main/DEBUG] [mixin/]: Mixing inject.MixinBucketItem from architectury-common.mixins.json into net.minecraft.world.item.BucketItem
-[24nov.2025 23:48:52.401] [main/DEBUG] [mixin/]: Mixing inject.MixinItemProperties from architectury-common.mixins.json into net.minecraft.world.item.Item$Properties
-[24nov.2025 23:48:52.887] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/Spider
-[24nov.2025 23:48:52.937] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/Zombie
-[24nov.2025 23:48:52.956] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/ZombieVillager
-[24nov.2025 23:48:52.965] [main/DEBUG] [mixin/]: Mixing vanillacompat.MixinThrownTrident from simpleclouds.mixins.json into net.minecraft.world.entity.projectile.ThrownTrident
-[24nov.2025 23:48:52.988] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/Evoker$EvokerSummonSpellGoal
-[24nov.2025 23:48:53.048] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/animal/horse/SkeletonTrapGoal
-[24nov.2025 23:48:53.056] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/Strider
-[24nov.2025 23:48:53.109] [main/DEBUG] [mixin/]: Mixing MixinServerPlayer from glitchcore.forge.mixins.json into net.minecraft.server.level.ServerPlayer
-[24nov.2025 23:48:53.127] [main/DEBUG] [mixin/]: Mixing ChunkAccessMixin from tectonic.mixins.json into net.minecraft.world.level.chunk.ChunkAccess
-[24nov.2025 23:48:53.132] [main/DEBUG] [mixin/]: Mixing LevelChunkSnowMixin from sereneseasonsplus.mixins.json into net.minecraft.world.level.chunk.LevelChunk
-[24nov.2025 23:48:53.150] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/npc/Villager
-[24nov.2025 23:48:53.218] [main/DEBUG] [mixin/]: Mixing inject.MixinFoodPropertiesBuilder from architectury-common.mixins.json into net.minecraft.world.food.FoodProperties$Builder
-[24nov.2025 23:48:53.220] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/effect/MobEffectInstance
-[24nov.2025 23:48:53.377] [main/DEBUG] [mixin/]: Mixing HeightmapMixin from tectonic.mixins.json into net.minecraft.world.level.levelgen.Heightmap
-[24nov.2025 23:48:53.482] [main/DEBUG] [mixin/]: Mixing darkness.DimensionTypeMixin from chloride.mixin.json into net.minecraft.world.level.dimension.DimensionType
-[24nov.2025 23:48:53.483] [main/DEBUG] [mixin/]: Mixing DimensionTypeAccessor from tectonic.mixins.json into net.minecraft.world.level.dimension.DimensionType
-[24nov.2025 23:48:53.522] [main/DEBUG] [mixin/]: Mixing common.PlacedFeatureAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.placement.PlacedFeature
-[24nov.2025 23:48:53.530] [main/DEBUG] [mixin/]: Mixing common.StructureProcessorListAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList
-[24nov.2025 23:48:53.566] [main/DEBUG] [mixin/]: Mixing common.StructureSetAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.StructureSet
-[24nov.2025 23:48:53.569] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/Structure
-[24nov.2025 23:48:53.574] [main/DEBUG] [mixin/]: Mixing StructurePieceMixin from tectonic.mixins.json into net.minecraft.world.level.levelgen.structure.StructurePiece
-[24nov.2025 23:48:53.589] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces$OceanRuinPiece
-[24nov.2025 23:48:53.594] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece
-[24nov.2025 23:48:53.597] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces$OceanMonumentPiece
-[24nov.2025 23:48:53.603] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces$WoodlandMansionPiece
-[24nov.2025 23:48:53.606] [main/DEBUG] [mixin/]: Mixing common.ShipwreckPieceMixin from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.structures.ShipwreckPieces$ShipwreckPiece
-[24nov.2025 23:48:53.618] [main/DEBUG] [mixin/]: Mixing common.JigsawStructureMixin from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.structures.JigsawStructure
-[24nov.2025 23:48:53.619] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/Structure
-[24nov.2025 23:48:53.627] [main/DEBUG] [mixin/]: Mixing common.StructureTemplatePoolAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool
-[24nov.2025 23:48:53.630] [main/DEBUG] [mixin/]: Mixing common.StructureTemplatePoolMixin from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool
-[24nov.2025 23:48:53.631] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$compileRawTemplates$0(Lcom/mojang/datafixers/util/Pair;)V to mdd47aa8$lambda$compileRawTemplates$0$0 in lithostitched.mixins.json:common.StructureTemplatePoolMixin
-[24nov.2025 23:48:53.664] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/biome/Biome
-[24nov.2025 23:48:53.664] [main/DEBUG] [mixin/]: Mixing features.world.biome.BiomeMixin from embeddium.mixins.json into net.minecraft.world.level.biome.Biome
-[24nov.2025 23:48:53.665] [main/DEBUG] [mixin/]: Mixing MixinBiome from sereneseasons.mixins.json into net.minecraft.world.level.biome.Biome
-[24nov.2025 23:48:53.666] [main/DEBUG] [mixin/]: Mixing client.MixinBiomeClient from sereneseasons.mixins.json into net.minecraft.world.level.biome.Biome
-[24nov.2025 23:48:53.666] [main/DEBUG] [mixin/]: Mixing BiomeMixin from tectonic.mixins.json into net.minecraft.world.level.biome.Biome
-[24nov.2025 23:48:53.680] [main/DEBUG] [mixin/]: Mixing features.render.world.ClientLevelMixin from embeddium.mixins.json into net.minecraft.client.multiplayer.ClientLevel
-[24nov.2025 23:48:53.681] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$new$0(Lnet/minecraft/world/level/biome/AmbientParticleSettings;)V to mdd47aa8$lambda$new$0$0 in embeddium.mixins.json:features.render.world.ClientLevelMixin
-[24nov.2025 23:48:53.682] [main/DEBUG] [mixin/]: Mixing MixinClientLevel from simpleclouds.mixins.json into net.minecraft.client.multiplayer.ClientLevel
-[24nov.2025 23:48:53.685] [main/DEBUG] [mixin/]: Mixing MixinClientLevel from architectury.mixins.json into net.minecraft.client.multiplayer.ClientLevel
-[24nov.2025 23:48:53.685] [main/DEBUG] [mixin/]: Mixing core.world.biome.ClientWorldMixin from embeddium.mixins.json into net.minecraft.client.multiplayer.ClientLevel
-[24nov.2025 23:48:53.685] [main/DEBUG] [mixin/]: Mixing core.world.map.ClientWorldMixin from embeddium.mixins.json into net.minecraft.client.multiplayer.ClientLevel
-[24nov.2025 23:48:53.686] [main/DEBUG] [mixin/]: Mixing features.render.world.sky.ClientWorldMixin from embeddium.mixins.json into net.minecraft.client.multiplayer.ClientLevel
-[24nov.2025 23:48:53.686] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$redirectSampleColor$0(Lnet/minecraft/world/level/Level;III)I to mdd47aa8$lambda$redirectSampleColor$0$1 in embeddium.mixins.json:features.render.world.sky.ClientWorldMixin
-[24nov.2025 23:48:53.715] [main/DEBUG] [mixin/]: Mixing TemperatureModifierMixin from tectonic.mixins.json into net.minecraft.world.level.biome.Biome$TemperatureModifier$2
-[24nov.2025 23:48:53.752] [main/DEBUG] [mixin/]: Mixing NoiseBasedChunkGeneratorMixin from tectonic.mixins.json into net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator
-[24nov.2025 23:48:53.753] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$tectonic$fixLavaLevel$2(Lnet/minecraft/core/Holder;)Lnet/minecraft/world/level/levelgen/Aquifer$FluidPicker; to mdd47aa8$lambda$tectonic$fixLavaLevel$2$0 in tectonic.mixins.json:NoiseBasedChunkGeneratorMixin
-[24nov.2025 23:48:53.753] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$tectonic$fixLavaLevel$1(Lnet/minecraft/world/level/levelgen/Aquifer$FluidStatus;IILnet/minecraft/world/level/levelgen/Aquifer$FluidStatus;Lnet/minecraft/world/level/levelgen/Aquifer$FluidStatus;III)Lnet/minecraft/world/level/levelgen/Aquifer$FluidStatus; to mdd47aa8$lambda$tectonic$fixLavaLevel$1$1 in tectonic.mixins.json:NoiseBasedChunkGeneratorMixin
-[24nov.2025 23:48:53.753] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$tectonic$fixLavaLevel$0(Lnet/minecraft/resources/ResourceKey;)Ljava/lang/Boolean; to mdd47aa8$lambda$tectonic$fixLavaLevel$0$2 in tectonic.mixins.json:NoiseBasedChunkGeneratorMixin
-[24nov.2025 23:48:53.765] [main/DEBUG] [mixin/]: Mixing common.NoiseGeneratorSettingsAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.NoiseGeneratorSettings
-[24nov.2025 23:48:53.770] [main/DEBUG] [mixin/]: Mixing NoiseSettingsAccessor from tectonic.mixins.json into net.minecraft.world.level.levelgen.NoiseSettings
-[24nov.2025 23:48:53.833] [main/DEBUG] [mixin/]: Mixing common.SinglePoolElementAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement
-[24nov.2025 23:48:53.882] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Creating vanilla freeze snapshot
-[24nov.2025 23:48:53.886] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:block Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.893] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:fluid Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.893] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:item Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.897] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:mob_effect Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.897] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:sound_event Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.900] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:potion Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.900] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:enchantment Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.900] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:entity_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.900] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:block_entity_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.900] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:particle_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.901] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:menu Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.901] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:painting_variant Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.901] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:recipe_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.901] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:recipe_serializer Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.901] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:attribute Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.901] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:stat_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.901] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:command_argument_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.901] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:villager_profession Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.901] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:point_of_interest_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.902] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:memory_module_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.902] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:sensor_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.902] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:schedule Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.902] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:activity Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.902] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/carver Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.903] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/feature Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.903] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:chunk_status Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.903] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/block_state_provider_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.903] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/foliage_placer_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.903] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/tree_decorator_type Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.903] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/biome Sync: VANILLA -> ACTIVE
-[24nov.2025 23:48:53.907] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Vanilla freeze snapshot created
-[24nov.2025 23:48:53.934] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: -Dio.netty.noUnsafe: false
-[24nov.2025 23:48:53.935] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: Java version: 17
-[24nov.2025 23:48:53.935] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: sun.misc.Unsafe.theUnsafe: available
-[24nov.2025 23:48:53.936] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: sun.misc.Unsafe.copyMemory: available
-[24nov.2025 23:48:53.936] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: sun.misc.Unsafe.storeFence: available
-[24nov.2025 23:48:53.936] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: java.nio.Buffer.address: available
-[24nov.2025 23:48:53.936] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: direct buffer constructor: unavailable
+[12mai2026 16:10:21.587] [main/INFO] [cpw.mods.modlauncher.Launcher/MODLAUNCHER]: ModLauncher running: args [--launchTarget, forgedatauserdev, --assetIndex, 5, --assetsDir, C:\Users\matga\.gradle\caches\forge_gradle\assets, --gameDir, ., --fml.forgeVersion, 47.4.13, --fml.mcVersion, 1.20.1, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20230612.114412, --mod, projectatmosphere, --all, --output, G:\Project-Atmosphere\src\generated\resources, --existing, G:\Project-Atmosphere\src\main\resources, --mixin.config, projectatmosphere.mixins.json]
+[12mai2026 16:10:21.591] [main/INFO] [cpw.mods.modlauncher.Launcher/MODLAUNCHER]: ModLauncher 10.0.9+10.0.9+main.dcd20f30 starting: java version 17.0.17 by Eclipse Adoptium; OS Windows 11 arch amd64 version 10.0
+[12mai2026 16:10:21.783] [main/DEBUG] [cpw.mods.modlauncher.LaunchServiceHandler/MODLAUNCHER]: Found launch services [fmlclientdev,forgeclient,minecraft,forgegametestserverdev,fmlserveruserdev,fmlclient,fmldatauserdev,forgeserverdev,forgeserveruserdev,forgeclientdev,forgeclientuserdev,forgeserver,forgedatadev,fmlserver,fmlclientuserdev,fmlserverdev,forgedatauserdev,testharness,forgegametestserveruserdev]
+[12mai2026 16:10:21.823] [main/DEBUG] [cpw.mods.modlauncher.NameMappingServiceHandler/MODLAUNCHER]: Found naming services : [srgtomcp]
+[12mai2026 16:10:21.857] [main/DEBUG] [cpw.mods.modlauncher.LaunchPluginHandler/MODLAUNCHER]: Found launch plugins: [mixin,eventbus,slf4jfixer,object_holder_definalize,runtime_enum_extender,capability_token_subclass,accesstransformer,runtimedistcleaner]
+[12mai2026 16:10:21.866] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Discovering transformation services
+[12mai2026 16:10:21.871] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path GAMEDIR is G:\Project-Atmosphere\run-data
+[12mai2026 16:10:21.871] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path MODSDIR is G:\Project-Atmosphere\run-data\mods
+[12mai2026 16:10:21.871] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path CONFIGDIR is G:\Project-Atmosphere\run-data\config
+[12mai2026 16:10:21.872] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path FMLCONFIG is G:\Project-Atmosphere\run-data\config\fml.toml
+[12mai2026 16:10:22.000] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Found additional transformation services from discovery services:
+[12mai2026 16:10:22.009] [main/INFO] [net.minecraftforge.fml.loading.ImmediateWindowHandler/]: ImmediateWindowProvider not loading because launch target is forgedatauserdev
+[12mai2026 16:10:22.023] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Found transformer services : [mixin,fml]
+[12mai2026 16:10:22.054] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Transformation services loading
+[12mai2026 16:10:22.055] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Loading service mixin
+[12mai2026 16:10:22.055] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Loaded service mixin
+[12mai2026 16:10:22.055] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Loading service fml
+[12mai2026 16:10:22.057] [main/DEBUG] [net.minecraftforge.fml.loading.LauncherVersion/CORE]: Found FMLLauncher version 1.0
+[12mai2026 16:10:22.057] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML 1.0 loading
+[12mai2026 16:10:22.058] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML found ModLauncher version : 10.0.9+10.0.9+main.dcd20f30
+[12mai2026 16:10:22.058] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Requesting CoreMods to not apply the fix for ASMAPI.findFirstInstructionBefore by default
+[12mai2026 16:10:22.059] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML found AccessTransformer version : 8.0.4+66+master.c09db6d7
+[12mai2026 16:10:22.061] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML found EventBus version : 6.0.5+6.0.5+master.eb8e549b
+[12mai2026 16:10:22.061] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Found Runtime Dist Cleaner
+[12mai2026 16:10:22.064] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/]: CoreMods will preserve legacy behavior of ASMAPI.findFirstInstructionBefore for backwards-compatibility
+[12mai2026 16:10:22.065] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: FML found CoreMod version : 5.2.4
+[12mai2026 16:10:22.066] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Found ForgeSPI package implementation version 7.0.1+7.0.1+master.d2b38bf6
+[12mai2026 16:10:22.066] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Found ForgeSPI package specification 5
+[12mai2026 16:10:22.066] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Loaded service fml
+[12mai2026 16:10:22.067] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Configuring option handling for services
+[12mai2026 16:10:22.090] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Transformation services initializing
+[12mai2026 16:10:22.093] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initializing transformation service mixin
+[12mai2026 16:10:22.119] [main/DEBUG] [mixin/]: MixinService [ModLauncher] was successfully booted in cpw.mods.cl.ModuleClassLoader@5e82df6a
+[12mai2026 16:10:22.144] [main/INFO] [mixin/]: SpongePowered MIXIN Subsystem Version=0.8.5 Source=union:/C:/Users/matga/.gradle/caches/modules-2/files-2.1/org.spongepowered/mixin/0.8.5/9d1c0c3a304ae6697ecd477218fa61b850bf57fc/mixin-0.8.5.jar%23129!/ Service=ModLauncher Env=CLIENT
+[12mai2026 16:10:22.149] [main/DEBUG] [mixin/]: Initialising Mixin Platform Manager
+[12mai2026 16:10:22.150] [main/DEBUG] [mixin/]: Adding mixin platform agents for container ModLauncher Root Container(ModLauncher:4f56a0a2)
+[12mai2026 16:10:22.150] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for ModLauncher Root Container(ModLauncher:4f56a0a2)
+[12mai2026 16:10:22.151] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container ModLauncher Root Container(ModLauncher:4f56a0a2)
+[12mai2026 16:10:22.152] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for ModLauncher Root Container(ModLauncher:4f56a0a2)
+[12mai2026 16:10:22.152] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container ModLauncher Root Container(ModLauncher:4f56a0a2)
+[12mai2026 16:10:22.163] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initialized transformation service mixin
+[12mai2026 16:10:22.164] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initializing transformation service fml
+[12mai2026 16:10:22.164] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Setting up basic FML game directories
+[12mai2026 16:10:22.164] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path GAMEDIR is G:\Project-Atmosphere\run-data
+[12mai2026 16:10:22.165] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path MODSDIR is G:\Project-Atmosphere\run-data\mods
+[12mai2026 16:10:22.165] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path CONFIGDIR is G:\Project-Atmosphere\run-data\config
+[12mai2026 16:10:22.165] [main/DEBUG] [net.minecraftforge.fml.loading.FMLPaths/CORE]: Path FMLCONFIG is G:\Project-Atmosphere\run-data\config\fml.toml
+[12mai2026 16:10:22.165] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Loading configuration
+[12mai2026 16:10:22.169] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Preparing ModFile
+[12mai2026 16:10:22.173] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Preparing launch handler
+[12mai2026 16:10:22.173] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Using forgedatauserdev as launch service
+[12mai2026 16:10:22.187] [main/DEBUG] [net.minecraftforge.fml.loading.FMLLoader/CORE]: Received command line version data : VersionInfo[forgeVersion=47.4.13, mcVersion=1.20.1, mcpVersion=20230612.114412, forgeGroup=net.minecraftforge]
+[12mai2026 16:10:22.189] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initialized transformation service fml
+[12mai2026 16:10:22.190] [main/DEBUG] [cpw.mods.modlauncher.NameMappingServiceHandler/MODLAUNCHER]: Current naming domain is 'mcp'
+[12mai2026 16:10:22.191] [main/DEBUG] [cpw.mods.modlauncher.NameMappingServiceHandler/MODLAUNCHER]: Identified name mapping providers {srg=srgtomcp:1234}
+[12mai2026 16:10:22.191] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Transformation services begin scanning
+[12mai2026 16:10:22.193] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Beginning scan trigger - transformation service mixin
+[12mai2026 16:10:22.193] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: End scan trigger - transformation service mixin
+[12mai2026 16:10:22.194] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Beginning scan trigger - transformation service fml
+[12mai2026 16:10:22.194] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Initiating mod scan
+[12mai2026 16:10:22.210] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModListHandler/CORE]: Found mod coordinates from lists: []
+[12mai2026 16:10:22.215] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer/CORE]: Found Mod Locators : (mods folder:null),(maven libs:null),(exploded directory:null),(minecraft:null),(userdev classpath:null)
+[12mai2026 16:10:22.216] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer/CORE]: Found Dependency Locators : (JarInJar:null)
+[12mai2026 16:10:22.225] [main/DEBUG] [net.minecraftforge.fml.loading.targets.CommonLaunchHandler/CORE]: Got mod coordinates projectatmosphere%%G:\Project-Atmosphere\build\resources\main;projectatmosphere%%G:\Project-Atmosphere\build\classes\java\main from env
+[12mai2026 16:10:22.227] [main/DEBUG] [net.minecraftforge.fml.loading.targets.CommonLaunchHandler/CORE]: Found supplied mod coordinates [{projectatmosphere=[G:\Project-Atmosphere\build\resources\main, G:\Project-Atmosphere\build\classes\java\main]}]
+[12mai2026 16:10:22.494] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar with {minecraft} mods - versions {1.20.1}
+[12mai2026 16:10:22.498] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\javafmllanguage\1.20.1-47.4.13\cf01e2179f72bd2829c65f5226d7dd0cfb6c686\javafmllanguage-1.20.1-47.4.13.jar
+[12mai2026 16:10:22.499] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\javafmllanguage\1.20.1-47.4.13\cf01e2179f72bd2829c65f5226d7dd0cfb6c686\javafmllanguage-1.20.1-47.4.13.jar is missing mods.toml file
+[12mai2026 16:10:22.501] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\lowcodelanguage\1.20.1-47.4.13\e3f96129ea14eba84316f2d69816253e228e6e4c\lowcodelanguage-1.20.1-47.4.13.jar
+[12mai2026 16:10:22.501] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\lowcodelanguage\1.20.1-47.4.13\e3f96129ea14eba84316f2d69816253e228e6e4c\lowcodelanguage-1.20.1-47.4.13.jar is missing mods.toml file
+[12mai2026 16:10:22.504] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\mclanguage\1.20.1-47.4.13\85796906112d9b621a7b2996f8bfda49da991ef0\mclanguage-1.20.1-47.4.13.jar
+[12mai2026 16:10:22.505] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\mclanguage\1.20.1-47.4.13\85796906112d9b621a7b2996f8bfda49da991ef0\mclanguage-1.20.1-47.4.13.jar is missing mods.toml file
+[12mai2026 16:10:22.507] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\fmlcore\1.20.1-47.4.13\f77e3021d1eb31d61f8d1b92d18ed4fb3c2806e5\fmlcore-1.20.1-47.4.13.jar
+[12mai2026 16:10:22.508] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\fmlcore\1.20.1-47.4.13\f77e3021d1eb31d61f8d1b92d18ed4fb3c2806e5\fmlcore-1.20.1-47.4.13.jar is missing mods.toml file
+[12mai2026 16:10:22.510] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\build\resources\main
+[12mai2026 16:10:22.517] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file main with {projectatmosphere} mods - versions {0.9.0.0}
+[12mai2026 16:10:22.518] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate /
+[12mai2026 16:10:22.522] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file with {forge} mods - versions {47.4.13}
+[12mai2026 16:10:22.550] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\libs\tectonic-3.0.17-forge-1.20.1-dev.jar
+[12mai2026 16:10:22.551] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-3.0.17-forge-1.20.1-dev.jar with {tectonic} mods - versions {3.0.17}
+[12mai2026 16:10:22.553] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.554] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar with {crackerslib} mods - versions {1.20.1-0.4.6}
+[12mai2026 16:10:22.557] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\betterdays-895618\7013807_mapped_official_1.20.1\betterdays-895618-7013807_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.558] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file betterdays-895618-7013807_mapped_official_1.20.1.jar with {betterdays} mods - versions {3.3.4.4}
+[12mai2026 16:10:22.562] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-plus-1288843\7325179_mapped_official_1.20.1\serene-seasons-plus-1288843-7325179_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.562] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-plus-1288843-7325179_mapped_official_1.20.1.jar with {sereneseasonsplus} mods - versions {4.2.2}
+[12mai2026 16:10:22.564] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\gabous-libs-1367332\7519775_mapped_official_1.20.1\gabous-libs-1367332-7519775_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.566] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file gabous-libs-1367332-7519775_mapped_official_1.20.1.jar with {gaboulibs} mods - versions {1.7.1}
+[12mai2026 16:10:22.568] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\architectury-api-419699\5137938_mapped_official_1.20.1\architectury-api-419699-5137938_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.568] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file architectury-api-419699-5137938_mapped_official_1.20.1.jar with {architectury} mods - versions {9.2.14}
+[12mai2026 16:10:22.570] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\tectonic-686836\7197834_mapped_official_1.20.1\tectonic-686836-7197834_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.571] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-686836-7197834_mapped_official_1.20.1.jar with {tectonic} mods - versions {3.0.17}
+[12mai2026 16:10:22.574] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\lithostitched-936015\6742615_mapped_official_1.20.1\lithostitched-936015-6742615_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.575] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file lithostitched-936015-6742615_mapped_official_1.20.1.jar with {lithostitched} mods - versions {1.4.11}
+[12mai2026 16:10:22.577] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\simple-clouds-1121215\6928978_mapped_official_1.20.1\simple-clouds-1121215-6928978_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.578] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file simple-clouds-1121215-6928978_mapped_official_1.20.1.jar with {simpleclouds} mods - versions {0.7.3+1.20.1-forge}
+[12mai2026 16:10:22.581] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\distant-horizons-508933\7948170_mapped_official_1.20.1\distant-horizons-508933-7948170_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.583] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file distant-horizons-508933-7948170_mapped_official_1.20.1.jar with {distanthorizons} mods - versions {3.0.1-b}
+[12mai2026 16:10:22.585] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-291874\6398227_mapped_official_1.20.1\serene-seasons-291874-6398227_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.586] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-291874-6398227_mapped_official_1.20.1.jar with {sereneseasons} mods - versions {9.1.0.2}
+[12mai2026 16:10:22.588] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\glitchcore-955399\5787839_mapped_official_1.20.1\glitchcore-955399-5787839_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.589] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file glitchcore-955399-5787839_mapped_official_1.20.1.jar with {glitchcore} mods - versions {0.0.1.1}
+[12mai2026 16:10:22.593] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\spark-361579\4738952_mapped_official_1.20.1\spark-361579-4738952_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.593] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file spark-361579-4738952_mapped_official_1.20.1.jar with {spark} mods - versions {1.10.53}
+[12mai2026 16:10:22.596] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\cloth-config-348521\5729105_mapped_official_1.20.1\cloth-config-348521-5729105_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.596] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file cloth-config-348521-5729105_mapped_official_1.20.1.jar with {cloth_config} mods - versions {11.1.136}
+[12mai2026 16:10:22.599] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\chloride-931925\6615986_mapped_official_1.20.1\chloride-931925-6615986_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.599] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file chloride-931925-6615986_mapped_official_1.20.1.jar with {chloride} mods - versions {1.7.2}
+[12mai2026 16:10:22.602] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\embeddium-908741\5681725_mapped_official_1.20.1\embeddium-908741-5681725_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.602] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file embeddium-908741-5681725_mapped_official_1.20.1.jar with {embeddium,rubidium} mods - versions {0.3.31+mc1.20.1,0.7.1}
+[12mai2026 16:10:22.605] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\mezz\jei\jei-1.20.1-forge\15.8.2.26_mapped_official_1.20.1\jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.606] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar with {jei} mods - versions {15.8.2.26}
+[12mai2026 16:10:22.609] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\jade-324717\6271651_mapped_official_1.20.1\jade-324717-6271651_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.612] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jade-324717-6271651_mapped_official_1.20.1.jar with {jade} mods - versions {11.13.1+forge}
+[12mai2026 16:10:22.614] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\libs\tectonic-3.0.17-forge-1.20.1-dev.jar
+[12mai2026 16:10:22.614] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-3.0.17-forge-1.20.1-dev.jar with {tectonic} mods - versions {3.0.17}
+[12mai2026 16:10:22.616] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.617] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar with {crackerslib} mods - versions {1.20.1-0.4.6}
+[12mai2026 16:10:22.625] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\betterdays-895618\7013807_mapped_official_1.20.1\betterdays-895618-7013807_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.626] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file betterdays-895618-7013807_mapped_official_1.20.1.jar with {betterdays} mods - versions {3.3.4.4}
+[12mai2026 16:10:22.628] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-plus-1288843\7325179_mapped_official_1.20.1\serene-seasons-plus-1288843-7325179_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.629] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-plus-1288843-7325179_mapped_official_1.20.1.jar with {sereneseasonsplus} mods - versions {4.2.2}
+[12mai2026 16:10:22.631] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\gabous-libs-1367332\7519775_mapped_official_1.20.1\gabous-libs-1367332-7519775_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.631] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file gabous-libs-1367332-7519775_mapped_official_1.20.1.jar with {gaboulibs} mods - versions {1.7.1}
+[12mai2026 16:10:22.633] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\architectury-api-419699\5137938_mapped_official_1.20.1\architectury-api-419699-5137938_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.634] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file architectury-api-419699-5137938_mapped_official_1.20.1.jar with {architectury} mods - versions {9.2.14}
+[12mai2026 16:10:22.636] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\tectonic-686836\7197834_mapped_official_1.20.1\tectonic-686836-7197834_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.636] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-686836-7197834_mapped_official_1.20.1.jar with {tectonic} mods - versions {3.0.17}
+[12mai2026 16:10:22.638] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\lithostitched-936015\6742615_mapped_official_1.20.1\lithostitched-936015-6742615_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.638] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file lithostitched-936015-6742615_mapped_official_1.20.1.jar with {lithostitched} mods - versions {1.4.11}
+[12mai2026 16:10:22.640] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\simple-clouds-1121215\6928978_mapped_official_1.20.1\simple-clouds-1121215-6928978_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.641] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file simple-clouds-1121215-6928978_mapped_official_1.20.1.jar with {simpleclouds} mods - versions {0.7.3+1.20.1-forge}
+[12mai2026 16:10:22.643] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\distant-horizons-508933\7948170_mapped_official_1.20.1\distant-horizons-508933-7948170_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.644] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file distant-horizons-508933-7948170_mapped_official_1.20.1.jar with {distanthorizons} mods - versions {3.0.1-b}
+[12mai2026 16:10:22.645] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-291874\6398227_mapped_official_1.20.1\serene-seasons-291874-6398227_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.646] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-291874-6398227_mapped_official_1.20.1.jar with {sereneseasons} mods - versions {9.1.0.2}
+[12mai2026 16:10:22.647] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\glitchcore-955399\5787839_mapped_official_1.20.1\glitchcore-955399-5787839_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.648] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file glitchcore-955399-5787839_mapped_official_1.20.1.jar with {glitchcore} mods - versions {0.0.1.1}
+[12mai2026 16:10:22.652] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\spark-361579\4738952_mapped_official_1.20.1\spark-361579-4738952_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.652] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file spark-361579-4738952_mapped_official_1.20.1.jar with {spark} mods - versions {1.10.53}
+[12mai2026 16:10:22.653] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\cloth-config-348521\5729105_mapped_official_1.20.1\cloth-config-348521-5729105_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.655] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file cloth-config-348521-5729105_mapped_official_1.20.1.jar with {cloth_config} mods - versions {11.1.136}
+[12mai2026 16:10:22.657] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\chloride-931925\6615986_mapped_official_1.20.1\chloride-931925-6615986_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.657] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file chloride-931925-6615986_mapped_official_1.20.1.jar with {chloride} mods - versions {1.7.2}
+[12mai2026 16:10:22.659] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\embeddium-908741\5681725_mapped_official_1.20.1\embeddium-908741-5681725_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.660] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file embeddium-908741-5681725_mapped_official_1.20.1.jar with {embeddium,rubidium} mods - versions {0.3.31+mc1.20.1,0.7.1}
+[12mai2026 16:10:22.662] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\mezz\jei\jei-1.20.1-forge\15.8.2.26_mapped_official_1.20.1\jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.663] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar with {jei} mods - versions {15.8.2.26}
+[12mai2026 16:10:22.665] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\jade-324717\6271651_mapped_official_1.20.1\jade-324717-6271651_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.665] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jade-324717-6271651_mapped_official_1.20.1.jar with {jade} mods - versions {11.13.1+forge}
+[12mai2026 16:10:22.669] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid sereneseasons, selecting most recent based on version data
+[12mai2026 16:10:22.669] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file serene-seasons-291874-6398227_mapped_official_1.20.1.jar for modid sereneseasons with version 9.1.0.2
+[12mai2026 16:10:22.669] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid betterdays, selecting most recent based on version data
+[12mai2026 16:10:22.669] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file betterdays-895618-7013807_mapped_official_1.20.1.jar for modid betterdays with version 3.3.4.4
+[12mai2026 16:10:22.669] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid crackerslib, selecting most recent based on version data
+[12mai2026 16:10:22.669] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar for modid crackerslib with version 1.20.1-0.4.6
+[12mai2026 16:10:22.669] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid glitchcore, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file glitchcore-955399-5787839_mapped_official_1.20.1.jar for modid glitchcore with version 0.0.1.1
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid jade, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file jade-324717-6271651_mapped_official_1.20.1.jar for modid jade with version 11.13.1+forge
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid simpleclouds, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file simple-clouds-1121215-6928978_mapped_official_1.20.1.jar for modid simpleclouds with version 0.7.3+1.20.1-forge
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid architectury, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file architectury-api-419699-5137938_mapped_official_1.20.1.jar for modid architectury with version 9.2.14
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid distanthorizons, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file distant-horizons-508933-7948170_mapped_official_1.20.1.jar for modid distanthorizons with version 3.0.1-b
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid jei, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar for modid jei with version 15.8.2.26
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid lithostitched, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file lithostitched-936015-6742615_mapped_official_1.20.1.jar for modid lithostitched with version 1.4.11
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid cloth_config, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file cloth-config-348521-5729105_mapped_official_1.20.1.jar for modid cloth_config with version 11.1.136
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid spark, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file spark-361579-4738952_mapped_official_1.20.1.jar for modid spark with version 1.10.53
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid gaboulibs, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file gabous-libs-1367332-7519775_mapped_official_1.20.1.jar for modid gaboulibs with version 1.7.1
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid chloride, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file chloride-931925-6615986_mapped_official_1.20.1.jar for modid chloride with version 1.7.2
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid embeddium, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file embeddium-908741-5681725_mapped_official_1.20.1.jar for modid embeddium with version 0.3.31+mc1.20.1
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid sereneseasonsplus, selecting most recent based on version data
+[12mai2026 16:10:22.670] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file serene-seasons-plus-1288843-7325179_mapped_official_1.20.1.jar for modid sereneseasonsplus with version 4.2.2
+[12mai2026 16:10:22.671] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 4 mods for first modid tectonic, selecting most recent based on version data
+[12mai2026 16:10:22.671] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file tectonic-3.0.17-forge-1.20.1-dev.jar for modid tectonic with version 3.0.17
+[12mai2026 16:10:22.674] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from serene-seasons-291874-6398227_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.674] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar, it does not contain dependency information.
+[12mai2026 16:10:22.674] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.674] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from glitchcore-955399-5787839_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.675] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from jade-324717-6271651_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.675] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from architectury-api-419699-5137938_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.675] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from distant-horizons-508933-7948170_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.675] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.675] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from lithostitched-936015-6742615_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.675] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from cloth-config-348521-5729105_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.675] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from spark-361579-4738952_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.675] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from gabous-libs-1367332-7519775_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from , it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from simplecloudsapi-forge-1.4-1.20.1_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from chloride-931925-6615986_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from main, it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from serene-seasons-plus-1288843-7325179_mapped_official_1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from tectonic-3.0.17-forge-1.20.1-dev.jar, it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from mclanguage-1.20.1-47.4.13.jar, it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from javafmllanguage-1.20.1-47.4.13.jar, it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from fmlcore-1.20.1-47.4.13.jar, it does not contain dependency information.
+[12mai2026 16:10:22.676] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from lowcodelanguage-1.20.1-47.4.13.jar, it does not contain dependency information.
+[12mai2026 16:10:22.718] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
+[12mai2026 16:10:22.720] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file crackerslib-forge-1.20.1-0.4.6.jar with {crackerslib} mods - versions {1.20.1-0.4.6}
+[12mai2026 16:10:22.725] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
+[12mai2026 16:10:22.725] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file whitenoise-1.20.1-forge-1.0.0.jar with {whitenoise} mods - versions {1.0.0}
+[12mai2026 16:10:22.727] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
+[12mai2026 16:10:22.728] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file mixinextras-forge-0.3.5.jar with {mixinextras} mods - versions {0.3.5}
+[12mai2026 16:10:22.728] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from crackerslib-forge-1.20.1-0.4.6.jar, it does not contain dependency information.
+[12mai2026 16:10:22.728] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from simplecloudsapi-forge-1.4-1.20.1.jar, it does not contain dependency information.
+[12mai2026 16:10:22.728] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from whitenoise-1.20.1-forge-1.0.0.jar, it does not contain dependency information.
+[12mai2026 16:10:22.733] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileDependencyLocator/]: Failed to load resource META-INF\jarjar\metadata.json from MixinExtras-0.3.5.jar, it does not contain dependency information.
+[12mai2026 16:10:22.749] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
+[12mai2026 16:10:22.750] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file whitenoise-1.20.1-forge-1.0.0.jar with {whitenoise} mods - versions {1.0.0}
+[12mai2026 16:10:22.754] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
+[12mai2026 16:10:22.754] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file mixinextras-forge-0.3.5.jar with {mixinextras} mods - versions {0.3.5}
+[12mai2026 16:10:22.756] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
+[12mai2026 16:10:22.757] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file crackerslib-forge-1.20.1-0.4.6.jar with {crackerslib} mods - versions {1.20.1-0.4.6}
+[12mai2026 16:10:22.759] [main/WARN] [net.minecraftforge.jarjar.selection.JarSelector/]: Attempted to select a dependency jar for JarJar which was passed in as source: crackerslib. Using Mod File: C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.759] [main/INFO] [net.minecraftforge.fml.loading.moddiscovery.JarInJarDependencyLocator/]: Found 4 dependencies adding them to mods collection
+[12mai2026 16:10:22.760] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid simplecloudsapi.forge, selecting most recent based on version data
+[12mai2026 16:10:22.760] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file simplecloudsapi-forge-1.4-1.20.1_mapped_official_1.20.1.jar for modid simplecloudsapi.forge with version 1.4-1.20.1
+[12mai2026 16:10:22.764] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar with {minecraft} mods - versions {1.20.1}
+[12mai2026 16:10:22.766] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\minecraft_user_repo\net\minecraftforge\forge\1.20.1-47.4.13_mapped_official_1.20.1\forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar with languages [LanguageSpec[languageName=minecraft, acceptedVersions=1]]
+[12mai2026 16:10:22.766] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\betterdays-895618\7013807_mapped_official_1.20.1\betterdays-895618-7013807_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.767] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file betterdays-895618-7013807_mapped_official_1.20.1.jar with {betterdays} mods - versions {3.3.4.4}
+[12mai2026 16:10:22.767] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\betterdays-895618\7013807_mapped_official_1.20.1\betterdays-895618-7013807_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
+[12mai2026 16:10:22.767] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-291874\6398227_mapped_official_1.20.1\serene-seasons-291874-6398227_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.768] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-291874-6398227_mapped_official_1.20.1.jar with {sereneseasons} mods - versions {9.1.0.2}
+[12mai2026 16:10:22.768] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-291874\6398227_mapped_official_1.20.1\serene-seasons-291874-6398227_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.768] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.768] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar with {crackerslib} mods - versions {1.20.1-0.4.6}
+[12mai2026 16:10:22.769] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.769] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\glitchcore-955399\5787839_mapped_official_1.20.1\glitchcore-955399-5787839_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.769] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file glitchcore-955399-5787839_mapped_official_1.20.1.jar with {glitchcore} mods - versions {0.0.1.1}
+[12mai2026 16:10:22.769] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\glitchcore-955399\5787839_mapped_official_1.20.1\glitchcore-955399-5787839_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.769] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\jade-324717\6271651_mapped_official_1.20.1\jade-324717-6271651_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.769] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jade-324717-6271651_mapped_official_1.20.1.jar with {jade} mods - versions {11.13.1+forge}
+[12mai2026 16:10:22.769] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\jade-324717\6271651_mapped_official_1.20.1\jade-324717-6271651_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
+[12mai2026 16:10:22.769] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\simple-clouds-1121215\6928978_mapped_official_1.20.1\simple-clouds-1121215-6928978_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.771] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file simple-clouds-1121215-6928978_mapped_official_1.20.1.jar with {simpleclouds} mods - versions {0.7.3+1.20.1-forge}
+[12mai2026 16:10:22.771] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\simple-clouds-1121215\6928978_mapped_official_1.20.1\simple-clouds-1121215-6928978_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.771] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
+[12mai2026 16:10:22.771] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file whitenoise-1.20.1-forge-1.0.0.jar with {whitenoise} mods - versions {1.0.0}
+[12mai2026 16:10:22.771] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
+[12mai2026 16:10:22.771] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\architectury-api-419699\5137938_mapped_official_1.20.1\architectury-api-419699-5137938_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.772] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file architectury-api-419699-5137938_mapped_official_1.20.1.jar with {architectury} mods - versions {9.2.14}
+[12mai2026 16:10:22.772] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\architectury-api-419699\5137938_mapped_official_1.20.1\architectury-api-419699-5137938_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
+[12mai2026 16:10:22.773] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\distant-horizons-508933\7948170_mapped_official_1.20.1\distant-horizons-508933-7948170_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.773] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file distant-horizons-508933-7948170_mapped_official_1.20.1.jar with {distanthorizons} mods - versions {3.0.1-b}
+[12mai2026 16:10:22.773] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\distant-horizons-508933\7948170_mapped_official_1.20.1\distant-horizons-508933-7948170_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=*]]
+[12mai2026 16:10:22.774] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\mezz\jei\jei-1.20.1-forge\15.8.2.26_mapped_official_1.20.1\jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.775] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar with {jei} mods - versions {15.8.2.26}
+[12mai2026 16:10:22.775] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\mezz\jei\jei-1.20.1-forge\15.8.2.26_mapped_official_1.20.1\jei-1.20.1-forge-15.8.2.26_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.775] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\lithostitched-936015\6742615_mapped_official_1.20.1\lithostitched-936015-6742615_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.775] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file lithostitched-936015-6742615_mapped_official_1.20.1.jar with {lithostitched} mods - versions {1.4.11}
+[12mai2026 16:10:22.776] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\lithostitched-936015\6742615_mapped_official_1.20.1\lithostitched-936015-6742615_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[46,)]]
+[12mai2026 16:10:22.776] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\spark-361579\4738952_mapped_official_1.20.1\spark-361579-4738952_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.776] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file spark-361579-4738952_mapped_official_1.20.1.jar with {spark} mods - versions {1.10.53}
+[12mai2026 16:10:22.776] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\spark-361579\4738952_mapped_official_1.20.1\spark-361579-4738952_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[34,)]]
+[12mai2026 16:10:22.776] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\cloth-config-348521\5729105_mapped_official_1.20.1\cloth-config-348521-5729105_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.777] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file cloth-config-348521-5729105_mapped_official_1.20.1.jar with {cloth_config} mods - versions {11.1.136}
+[12mai2026 16:10:22.777] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\cloth-config-348521\5729105_mapped_official_1.20.1\cloth-config-348521-5729105_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[34,)]]
+[12mai2026 16:10:22.777] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate /
+[12mai2026 16:10:22.777] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file with {forge} mods - versions {47.4.13}
+[12mai2026 16:10:22.777] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file / with languages [LanguageSpec[languageName=javafml, acceptedVersions=[24,]]]
+[12mai2026 16:10:22.779] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Found coremod field_to_method with Javascript path coremods/field_to_method.js
+[12mai2026 16:10:22.779] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Found coremod field_to_instanceof with Javascript path coremods/field_to_instanceof.js
+[12mai2026 16:10:22.779] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Found coremod add_bouncer_method with Javascript path coremods/add_bouncer_method.js
+[12mai2026 16:10:22.779] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Found coremod method_redirector with Javascript path coremods/method_redirector.js
+[12mai2026 16:10:22.779] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Found coremod coremods/field_to_method.js
+[12mai2026 16:10:22.780] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Found coremod coremods/field_to_instanceof.js
+[12mai2026 16:10:22.780] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Found coremod coremods/add_bouncer_method.js
+[12mai2026 16:10:22.780] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Found coremod coremods/method_redirector.js
+[12mai2026 16:10:22.780] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\gabous-libs-1367332\7519775_mapped_official_1.20.1\gabous-libs-1367332-7519775_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.780] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file gabous-libs-1367332-7519775_mapped_official_1.20.1.jar with {gaboulibs} mods - versions {1.7.1}
+[12mai2026 16:10:22.780] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\gabous-libs-1367332\7519775_mapped_official_1.20.1\gabous-libs-1367332-7519775_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.780] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\chloride-931925\6615986_mapped_official_1.20.1\chloride-931925-6615986_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.781] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file chloride-931925-6615986_mapped_official_1.20.1.jar with {chloride} mods - versions {1.7.2}
+[12mai2026 16:10:22.781] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\chloride-931925\6615986_mapped_official_1.20.1\chloride-931925-6615986_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,48)]]
+[12mai2026 16:10:22.781] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\embeddium-908741\5681725_mapped_official_1.20.1\embeddium-908741-5681725_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.781] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file embeddium-908741-5681725_mapped_official_1.20.1.jar with {embeddium,rubidium} mods - versions {0.3.31+mc1.20.1,0.7.1}
+[12mai2026 16:10:22.781] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\embeddium-908741\5681725_mapped_official_1.20.1\embeddium-908741-5681725_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.782] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\build\resources\main
+[12mai2026 16:10:22.783] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file main with {projectatmosphere} mods - versions {0.9.0.0}
+[12mai2026 16:10:22.783] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file G:\Project-Atmosphere\build\resources\main with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.783] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate G:\Project-Atmosphere\libs\tectonic-3.0.17-forge-1.20.1-dev.jar
+[12mai2026 16:10:22.783] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file tectonic-3.0.17-forge-1.20.1-dev.jar with {tectonic} mods - versions {3.0.17}
+[12mai2026 16:10:22.783] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file G:\Project-Atmosphere\libs\tectonic-3.0.17-forge-1.20.1-dev.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.783] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-plus-1288843\7325179_mapped_official_1.20.1\serene-seasons-plus-1288843-7325179_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.783] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file serene-seasons-plus-1288843-7325179_mapped_official_1.20.1.jar with {sereneseasonsplus} mods - versions {4.2.2}
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\curse\maven\serene-seasons-plus-1288843\7325179_mapped_official_1.20.1\serene-seasons-plus-1288843-7325179_mapped_official_1.20.1.jar with languages [LanguageSpec[languageName=javafml, acceptedVersions=[47,)]]
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file mixinextras-forge-0.3.5.jar with {mixinextras} mods - versions {0.3.5}
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages [LanguageSpec[languageName=javafml, acceptedVersions=*]]
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages []
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\simplecloudsapi-forge\1.4-1.20.1_mapped_official_1.20.1\simplecloudsapi-forge-1.4-1.20.1_mapped_official_1.20.1.jar with languages []
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Considering mod file candidate
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFileInfo/LOADING]: Found valid mod file mixinextras-forge-0.3.5.jar with {mixinextras} mods - versions {0.3.5}
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages [LanguageSpec[languageName=javafml, acceptedVersions=*]]
+[12mai2026 16:10:22.784] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file with languages []
+[12mai2026 16:10:22.785] [main/DEBUG] [net.minecraftforge.fml.loading.moddiscovery.ModFile/LOADING]: Loading mod file C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\simplecloudsapi-forge\1.4-1.20.1_mapped_official_1.20.1\simplecloudsapi-forge-1.4-1.20.1_mapped_official_1.20.1.jar with languages []
+[12mai2026 16:10:22.786] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: End scan trigger - transformation service fml
+[12mai2026 16:10:22.800] [main/DEBUG] [net.minecraftforge.fml.loading.LanguageLoadingProvider/CORE]: Found 3 language providers
+[12mai2026 16:10:22.800] [main/DEBUG] [net.minecraftforge.fml.loading.LanguageLoadingProvider/CORE]: Found language provider minecraft, version 1.0
+[12mai2026 16:10:22.802] [main/DEBUG] [net.minecraftforge.fml.loading.LanguageLoadingProvider/CORE]: Found language provider lowcodefml, version 47
+[12mai2026 16:10:22.802] [main/DEBUG] [net.minecraftforge.fml.loading.LanguageLoadingProvider/CORE]: Found language provider javafml, version 47
+[12mai2026 16:10:22.805] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid mixinextras, selecting most recent based on version data
+[12mai2026 16:10:22.805] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file mixinextras-forge-0.3.5.jar for modid mixinextras with version 0.3.5
+[12mai2026 16:10:22.805] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid MixinExtras, selecting most recent based on version data
+[12mai2026 16:10:22.806] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file MixinExtras-0.3.5.jar for modid MixinExtras with version 0.0NONE
+[12mai2026 16:10:22.806] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Found 2 mods for first modid simplecloudsapi.forge, selecting most recent based on version data
+[12mai2026 16:10:22.806] [main/DEBUG] [net.minecraftforge.fml.loading.UniqueModListBuilder/]: Selected file simplecloudsapi-forge-1.4-1.20.1_mapped_official_1.20.1.jar for modid simplecloudsapi.forge with version 1.4-1.20.1
+[12mai2026 16:10:22.808] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/]: Configured system mods: [minecraft, forge]
+[12mai2026 16:10:22.808] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/]: Found system mod: minecraft
+[12mai2026 16:10:22.808] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/]: Found system mod: forge
+[12mai2026 16:10:22.811] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/LOADING]: Found 45 mod requirements (37 mandatory, 8 optional)
+[12mai2026 16:10:22.811] [main/DEBUG] [net.minecraftforge.fml.loading.ModSorter/LOADING]: Found 0 mod requirements missing (0 mandatory, 0 optional)
+[12mai2026 16:10:23.153] [main/DEBUG] [net.minecraftforge.fml.loading.MCPNamingService/CORE]: Loaded 33222 method mappings from methods.csv
+[12mai2026 16:10:23.177] [main/DEBUG] [net.minecraftforge.fml.loading.MCPNamingService/CORE]: Loaded 31003 field mappings from fields.csv
+[12mai2026 16:10:23.238] [main/DEBUG] [cpw.mods.modlauncher.TransformationServicesHandler/MODLAUNCHER]: Transformation services loading transformers
+[12mai2026 16:10:23.239] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initializing transformers for transformation service mixin
+[12mai2026 16:10:23.240] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initialized transformers for transformation service mixin
+[12mai2026 16:10:23.241] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initializing transformers for transformation service fml
+[12mai2026 16:10:23.241] [main/DEBUG] [net.minecraftforge.fml.loading.FMLServiceProvider/CORE]: Loading coremod transformers
+[12mai2026 16:10:23.241] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: Loading CoreMod from coremods/field_to_method.js
+[12mai2026 16:10:23.584] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: CoreMod loaded successfully
+[12mai2026 16:10:23.584] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: Loading CoreMod from coremods/field_to_instanceof.js
+[12mai2026 16:10:23.704] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: CoreMod loaded successfully
+[12mai2026 16:10:23.704] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: Loading CoreMod from coremods/add_bouncer_method.js
+[12mai2026 16:10:23.746] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: CoreMod loaded successfully
+[12mai2026 16:10:23.746] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: Loading CoreMod from coremods/method_redirector.js
+[12mai2026 16:10:23.819] [main/DEBUG] [net.minecraftforge.coremod.CoreModEngine/COREMOD]: CoreMod loaded successfully
+[12mai2026 16:10:23.842] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@6824b913 to Target : CLASS {Lnet/minecraft/world/level/biome/Biome;} {} {V}
+[12mai2026 16:10:23.844] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@3c66b7d8 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/Structure;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@37e69c43 to Target : CLASS {Lnet/minecraft/world/effect/MobEffectInstance;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@5c7dfc05 to Target : CLASS {Lnet/minecraft/world/level/block/LiquidBlock;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@345d053b to Target : CLASS {Lnet/minecraft/world/item/BucketItem;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@3d0cac1f to Target : CLASS {Lnet/minecraft/world/level/block/StairBlock;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@3e8b3b79 to Target : CLASS {Lnet/minecraft/world/level/block/FlowerPotBlock;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@d257579 to Target : CLASS {Lnet/minecraft/world/item/ItemStack;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@518ddd3b to Target : CLASS {Lnet/minecraft/network/play/client/CClientSettingsPacket;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/monster/Zombie;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/monster/ZombieVillager;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/npc/Villager;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/monster/Spider;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/ai/village/VillageSiege;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/monster/Strider;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces$OceanMonumentPiece;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/npc/CatSpawner;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/level/levelgen/PhantomSpawner;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/animal/frog/Tadpole;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces$WoodlandMansionPiece;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/server/commands/RaidCommand;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/level/NaturalSpawner;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/level/levelgen/PatrolSpawner;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/EntityType;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces$OceanRuinPiece;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/raid/Raid;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/structures/SwampHutPiece;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/animal/horse/SkeletonTrapGoal;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/server/commands/SummonCommand;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformStore/MODLAUNCHER]: Adding transformer net.minecraftforge.coremod.transformer.CoreModClassTransformer@939ff41 to Target : CLASS {Lnet/minecraft/world/entity/monster/Evoker$EvokerSummonSpellGoal;} {} {V}
+[12mai2026 16:10:23.845] [main/DEBUG] [cpw.mods.modlauncher.TransformationServiceDecorator/MODLAUNCHER]: Initialized transformers for transformation service fml
+[12mai2026 16:10:24.474] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:ModLauncher Root Container(ModLauncher:4f56a0a2)]
+[12mai2026 16:10:24.474] [main/DEBUG] [mixin/]: Registering mixin config: projectatmosphere.mixins.json
+[12mai2026 16:10:24.507] [main/DEBUG] [mixin/]: Compatibility level JAVA_17 specified by projectatmosphere.mixins.json is higher than the maximum level supported by this version of mixin (JAVA_13).
+[12mai2026 16:10:24.512] [main/INFO] [mixin/]: Compatibility level set to JAVA_17
+[12mai2026 16:10:24.513] [main/ERROR] [mixin/]: Mixin config projectatmosphere.mixins.json does not specify "minVersion" property
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Processing launch tasks for PlatformAgent[MixinPlatformAgentDefault:ModLauncher Root Container(ModLauncher:4f56a0a2)]
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(betterdays)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(betterdays)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(betterdays)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(betterdays)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(betterdays)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(betterdays)]
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(minecraft)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(minecraft)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(minecraft)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(minecraft)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(minecraft)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(minecraft)]
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(crackerslib)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(crackerslib)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(crackerslib)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(crackerslib)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(crackerslib)
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(crackerslib)]
+[12mai2026 16:10:24.513] [main/DEBUG] [mixin/]: Registering mixin config: crackerslib.mixins.json
+[12mai2026 16:10:24.514] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(mixinextras)
+[12mai2026 16:10:24.514] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(mixinextras)
+[12mai2026 16:10:24.514] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(mixinextras)
+[12mai2026 16:10:24.514] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(mixinextras)
+[12mai2026 16:10:24.514] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(mixinextras)
+[12mai2026 16:10:24.514] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(mixinextras)]
+[12mai2026 16:10:24.514] [main/DEBUG] [mixin/]: Registering mixin config: mixinextras.init.mixins.json
+[12mai2026 16:10:24.516] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(glitchcore)
+[12mai2026 16:10:24.516] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(glitchcore)
+[12mai2026 16:10:24.516] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(glitchcore)
+[12mai2026 16:10:24.516] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(glitchcore)
+[12mai2026 16:10:24.516] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(glitchcore)
+[12mai2026 16:10:24.516] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(glitchcore)]
+[12mai2026 16:10:24.516] [main/DEBUG] [mixin/]: Registering mixin config: glitchcore.mixins.json
+[12mai2026 16:10:24.518] [main/DEBUG] [mixin/]: Registering mixin config: glitchcore.forge.mixins.json
+[12mai2026 16:10:24.519] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(sereneseasons)
+[12mai2026 16:10:24.519] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(sereneseasons)
+[12mai2026 16:10:24.519] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(sereneseasons)
+[12mai2026 16:10:24.520] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(sereneseasons)
+[12mai2026 16:10:24.520] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(sereneseasons)
+[12mai2026 16:10:24.520] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(sereneseasons)]
+[12mai2026 16:10:24.520] [main/DEBUG] [mixin/]: Registering mixin config: sereneseasons.mixins.json
+[12mai2026 16:10:24.520] [main/DEBUG] [mixin/]: Registering mixin config: sereneseasons.forge.mixins.json
+[12mai2026 16:10:24.521] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(jade)
+[12mai2026 16:10:24.521] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(jade)
+[12mai2026 16:10:24.521] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(jade)
+[12mai2026 16:10:24.521] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(jade)
+[12mai2026 16:10:24.521] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(jade)
+[12mai2026 16:10:24.521] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(jade)]
+[12mai2026 16:10:24.521] [main/DEBUG] [mixin/]: Registering mixin config: jade.mixins.json
+[12mai2026 16:10:24.522] [main/DEBUG] [mixin/]: Compatibility level JAVA_16 specified by jade.mixins.json is higher than the maximum level supported by this version of mixin (JAVA_13).
+[12mai2026 16:10:24.523] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(simpleclouds)
+[12mai2026 16:10:24.523] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(simpleclouds)
+[12mai2026 16:10:24.523] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(simpleclouds)
+[12mai2026 16:10:24.523] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(simpleclouds)
+[12mai2026 16:10:24.523] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(simpleclouds)
+[12mai2026 16:10:24.523] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(simpleclouds)]
+[12mai2026 16:10:24.523] [main/DEBUG] [mixin/]: Registering mixin config: simpleclouds.mixins.json
+[12mai2026 16:10:24.524] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(architectury)
+[12mai2026 16:10:24.524] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(architectury)
+[12mai2026 16:10:24.524] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(architectury)
+[12mai2026 16:10:24.524] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(architectury)
+[12mai2026 16:10:24.524] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(architectury)
+[12mai2026 16:10:24.524] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(architectury)]
+[12mai2026 16:10:24.524] [main/DEBUG] [mixin/]: Registering mixin config: architectury.mixins.json
+[12mai2026 16:10:24.524] [main/DEBUG] [mixin/]: Compatibility level JAVA_16 specified by architectury.mixins.json is higher than the maximum level supported by this version of mixin (JAVA_13).
+[12mai2026 16:10:24.524] [main/DEBUG] [mixin/]: Registering mixin config: architectury-common.mixins.json
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Compatibility level JAVA_16 specified by architectury-common.mixins.json is higher than the maximum level supported by this version of mixin (JAVA_13).
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(whitenoise)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(whitenoise)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(whitenoise)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(whitenoise)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(whitenoise)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(whitenoise)]
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(distanthorizons)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(distanthorizons)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(distanthorizons)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(distanthorizons)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(distanthorizons)
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(distanthorizons)]
+[12mai2026 16:10:24.527] [main/DEBUG] [mixin/]: Registering mixin config: DistantHorizons.forge.mixins.json
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(jei)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(jei)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(jei)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(jei)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(jei)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(jei)]
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(lithostitched)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(lithostitched)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(lithostitched)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(lithostitched)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(lithostitched)
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(lithostitched)]
+[12mai2026 16:10:24.529] [main/DEBUG] [mixin/]: Registering mixin config: lithostitched.mixins.json
+[12mai2026 16:10:24.531] [main/DEBUG] [mixin/]: Registering mixin config: lithostitched.forge.mixins.json
+[12mai2026 16:10:24.532] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(cloth_config)
+[12mai2026 16:10:24.532] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(cloth_config)
+[12mai2026 16:10:24.533] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(cloth_config)
+[12mai2026 16:10:24.533] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(cloth_config)
+[12mai2026 16:10:24.533] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(cloth_config)
+[12mai2026 16:10:24.533] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(cloth_config)]
+[12mai2026 16:10:24.533] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(spark)
+[12mai2026 16:10:24.533] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(spark)
+[12mai2026 16:10:24.533] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(spark)
+[12mai2026 16:10:24.533] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(spark)
+[12mai2026 16:10:24.533] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(spark)
+[12mai2026 16:10:24.534] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(spark)]
+[12mai2026 16:10:24.534] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(gaboulibs)
+[12mai2026 16:10:24.534] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(gaboulibs)
+[12mai2026 16:10:24.534] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(gaboulibs)
+[12mai2026 16:10:24.534] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(gaboulibs)
+[12mai2026 16:10:24.534] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(gaboulibs)
+[12mai2026 16:10:24.534] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(gaboulibs)]
+[12mai2026 16:10:24.534] [main/DEBUG] [mixin/]: Registering mixin config: gaboulibs.mixins.json
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(forge)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(forge)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(forge)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(forge)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(forge)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(forge)]
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(embeddium)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(embeddium)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(embeddium)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(embeddium)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(embeddium)
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(embeddium)]
+[12mai2026 16:10:24.535] [main/DEBUG] [mixin/]: Registering mixin config: embeddium.mixins.json
+[12mai2026 16:10:24.536] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(chloride)
+[12mai2026 16:10:24.536] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(chloride)
+[12mai2026 16:10:24.537] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(chloride)
+[12mai2026 16:10:24.537] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(chloride)
+[12mai2026 16:10:24.537] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(chloride)
+[12mai2026 16:10:24.537] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(chloride)]
+[12mai2026 16:10:24.537] [main/DEBUG] [mixin/]: Registering mixin config: chloride.mixin.json
+[12mai2026 16:10:24.538] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(projectatmosphere)
+[12mai2026 16:10:24.538] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(projectatmosphere)
+[12mai2026 16:10:24.538] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(projectatmosphere)
+[12mai2026 16:10:24.538] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(projectatmosphere)
+[12mai2026 16:10:24.538] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(projectatmosphere)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(projectatmosphere)]
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(sereneseasonsplus)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(sereneseasonsplus)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(sereneseasonsplus)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(sereneseasonsplus)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(sereneseasonsplus)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(sereneseasonsplus)]
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Registering mixin config: sereneseasonsplus.mixins.json
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(tectonic)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(tectonic)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(tectonic)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(tectonic)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(tectonic)
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(tectonic)]
+[12mai2026 16:10:24.539] [main/DEBUG] [mixin/]: Registering mixin config: tectonic.mixins.json
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: Registering mixin config: tectonic_1.20.1.mixins.json
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(MixinExtras)
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(MixinExtras)
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(MixinExtras)
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(MixinExtras)
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(MixinExtras)
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(MixinExtras)]
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: Adding mixin platform agents for container SecureJarResource(simplecloudsapi.forge)
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentMinecraftForge for SecureJarResource(simplecloudsapi.forge)
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: MixinPlatformAgentMinecraftForge rejected container SecureJarResource(simplecloudsapi.forge)
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: Instancing new MixinPlatformAgentDefault for SecureJarResource(simplecloudsapi.forge)
+[12mai2026 16:10:24.541] [main/DEBUG] [mixin/]: MixinPlatformAgentDefault accepted container SecureJarResource(simplecloudsapi.forge)
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing prepare() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(simplecloudsapi.forge)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: inject() running with 25 agents
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:ModLauncher Root Container(ModLauncher:4f56a0a2)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(betterdays)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(minecraft)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(crackerslib)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(mixinextras)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(glitchcore)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(sereneseasons)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(jade)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(simpleclouds)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(architectury)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(whitenoise)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(distanthorizons)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(jei)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(lithostitched)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(cloth_config)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(spark)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(gaboulibs)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(forge)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(embeddium)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(chloride)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(projectatmosphere)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(sereneseasonsplus)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(tectonic)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(MixinExtras)]
+[12mai2026 16:10:24.542] [main/DEBUG] [mixin/]: Processing inject() for PlatformAgent[MixinPlatformAgentDefault:SecureJarResource(simplecloudsapi.forge)]
+[12mai2026 16:10:24.543] [main/INFO] [cpw.mods.modlauncher.LaunchServiceHandler/MODLAUNCHER]: Launching target 'forgedatauserdev' with arguments [--gameDir, ., --assetsDir, C:\Users\matga\.gradle\caches\forge_gradle\assets, --assetIndex, 5, --mod, projectatmosphere, --all, --output, G:\Project-Atmosphere\src\generated\resources, --existing, G:\Project-Atmosphere\src\main\resources]
+[12mai2026 16:10:24.668] [main/DEBUG] [mixin/]: Error cleaning class output directory: .mixin.out
+[12mai2026 16:10:24.672] [main/DEBUG] [mixin/]: Preparing mixins for MixinEnvironment[DEFAULT]
+[12mai2026 16:10:24.672] [main/DEBUG] [mixin/]: Selecting config projectatmosphere.mixins.json
+[12mai2026 16:10:24.698] [main/WARN] [mixin/]: Reference map 'projectatmosphere.refmap.json' for projectatmosphere.mixins.json could not be read. If this is a development environment you can ignore this message
+[12mai2026 16:10:24.700] [main/DEBUG] [mixin/]: Selecting config crackerslib.mixins.json
+[12mai2026 16:10:24.932] [main/INFO] [mixin/]: Remapping refMap crackerslib.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:24.932] [main/DEBUG] [mixin/]: Selecting config mixinextras.init.mixins.json
+[12mai2026 16:10:24.997] [main/DEBUG] [MixinExtras|Service/]: com.llamalad7.mixinextras.service.MixinExtrasServiceImpl(version=0.3.5) is taking over from null
+[12mai2026 16:10:25.048] [main/DEBUG] [mixin/]: Registering new injector for @Inject with org.spongepowered.asm.mixin.injection.struct.CallbackInjectionInfo
+[12mai2026 16:10:25.049] [main/DEBUG] [mixin/]: Registering new injector for @ModifyArg with org.spongepowered.asm.mixin.injection.struct.ModifyArgInjectionInfo
+[12mai2026 16:10:25.049] [main/DEBUG] [mixin/]: Registering new injector for @ModifyArgs with org.spongepowered.asm.mixin.injection.struct.ModifyArgsInjectionInfo
+[12mai2026 16:10:25.051] [main/DEBUG] [mixin/]: Registering new injector for @Redirect with org.spongepowered.asm.mixin.injection.struct.RedirectInjectionInfo
+[12mai2026 16:10:25.052] [main/DEBUG] [mixin/]: Registering new injector for @ModifyVariable with org.spongepowered.asm.mixin.injection.struct.ModifyVariableInjectionInfo
+[12mai2026 16:10:25.052] [main/DEBUG] [mixin/]: Registering new injector for @ModifyConstant with org.spongepowered.asm.mixin.injection.struct.ModifyConstantInjectionInfo
+[12mai2026 16:10:25.062] [main/DEBUG] [mixin/]: Selecting config glitchcore.mixins.json
+[12mai2026 16:10:25.063] [main/INFO] [mixin/]: Remapping refMap glitchcore.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.063] [main/DEBUG] [mixin/]: Selecting config glitchcore.forge.mixins.json
+[12mai2026 16:10:25.064] [main/INFO] [mixin/]: Remapping refMap glitchcore.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.064] [main/DEBUG] [mixin/]: Selecting config sereneseasons.mixins.json
+[12mai2026 16:10:25.065] [main/INFO] [mixin/]: Remapping refMap sereneseasons.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.065] [main/DEBUG] [mixin/]: Selecting config sereneseasons.forge.mixins.json
+[12mai2026 16:10:25.066] [main/INFO] [mixin/]: Remapping refMap sereneseasons.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.066] [main/DEBUG] [mixin/]: Selecting config jade.mixins.json
+[12mai2026 16:10:25.066] [main/INFO] [mixin/]: Remapping refMap jade.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.066] [main/DEBUG] [mixin/]: Selecting config simpleclouds.mixins.json
+[12mai2026 16:10:25.067] [main/INFO] [mixin/]: Remapping refMap simpleclouds.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.068] [main/DEBUG] [mixin/]: Selecting config architectury.mixins.json
+[12mai2026 16:10:25.071] [main/INFO] [mixin/]: Remapping refMap architectury-forge-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.071] [main/DEBUG] [mixin/]: Selecting config architectury-common.mixins.json
+[12mai2026 16:10:25.072] [main/INFO] [mixin/]: Remapping refMap architectury-common-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.072] [main/DEBUG] [mixin/]: Selecting config DistantHorizons.forge.mixins.json
+[12mai2026 16:10:25.073] [main/INFO] [mixin/]: Remapping refMap DistantHorizons.forge.mixins-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.073] [main/DEBUG] [mixin/]: Selecting config lithostitched.mixins.json
+[12mai2026 16:10:25.074] [main/INFO] [mixin/]: Remapping refMap lithostitched.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.074] [main/DEBUG] [mixin/]: Selecting config lithostitched.forge.mixins.json
+[12mai2026 16:10:25.074] [main/INFO] [mixin/]: Remapping refMap lithostitched.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.074] [main/DEBUG] [mixin/]: Selecting config gaboulibs.mixins.json
+[12mai2026 16:10:25.076] [main/DEBUG] [mixin/]: Selecting config embeddium.mixins.json
+[12mai2026 16:10:25.086] [main/INFO] [Embeddium/]: Loaded configuration file for Embeddium: 67 options available, 0 override(s) found
+[12mai2026 16:10:25.089] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Searching for graphics cards...
+[12mai2026 16:10:25.251] [main/DEBUG] [oshi.util.FileUtil/]: No oshi.properties file found from ClassLoader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf
+[12mai2026 16:10:25.367] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Found graphics card: GraphicsAdapterInfo[vendor=UNKNOWN, name=Meta Virtual Monitor, version=DriverVersion=5.3.57.114]
+[12mai2026 16:10:25.367] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Found graphics card: GraphicsAdapterInfo[vendor=NVIDIA, name=NVIDIA GeForce RTX 4070 Ti SUPER, version=DriverVersion=32.0.15.9597]
+[12mai2026 16:10:25.371] [main/WARN] [Embeddium-Workarounds/]: Embeddium has applied one or more workarounds to prevent crashes or other issues on your system: [NVIDIA_THREADED_OPTIMIZATIONS]
+[12mai2026 16:10:25.371] [main/WARN] [Embeddium-Workarounds/]: This is not necessarily an issue, but it may result in certain features or optimizations being disabled. You can sometimes fix these issues by upgrading your graphics driver.
+[12mai2026 16:10:25.378] [main/INFO] [mixin/]: Remapping refMap embeddium-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.378] [main/DEBUG] [mixin/]: Selecting config chloride.mixin.json
+[12mai2026 16:10:25.381] [main/INFO] [mixin/]: Remapping refMap chloride.mixin-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.382] [main/DEBUG] [mixin/]: Selecting config sereneseasonsplus.mixins.json
+[12mai2026 16:10:25.388] [main/INFO] [mixin/]: Remapping refMap sereneseasonsplus-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.389] [main/DEBUG] [mixin/]: Selecting config tectonic.mixins.json
+[12mai2026 16:10:25.389] [main/DEBUG] [mixin/]: Selecting config tectonic_1.20.1.mixins.json
+[12mai2026 16:10:25.390] [main/DEBUG] [mixin/]: Preparing projectatmosphere.mixins.json (29)
+[12mai2026 16:10:25.432] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/biome/Biome
+[12mai2026 16:10:25.505] [main/DEBUG] [mixin/]: Preparing crackerslib.mixins.json (2)
+[12mai2026 16:10:25.510] [main/DEBUG] [mixin/]: Preparing mixinextras.init.mixins.json (0)
+[12mai2026 16:10:25.510] [main/DEBUG] [mixin/]: Preparing glitchcore.mixins.json (4)
+[12mai2026 16:10:25.513] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/item/ItemStack
+[12mai2026 16:10:25.535] [main/DEBUG] [mixin/]: Preparing glitchcore.forge.mixins.json (7)
+[12mai2026 16:10:25.545] [main/DEBUG] [mixin/]: Preparing sereneseasons.mixins.json (6)
+[12mai2026 16:10:25.561] [main/DEBUG] [mixin/]: Preparing sereneseasons.forge.mixins.json (0)
+[12mai2026 16:10:25.561] [main/DEBUG] [mixin/]: Preparing jade.mixins.json (2)
+[12mai2026 16:10:25.564] [main/DEBUG] [mixin/]: Preparing simpleclouds.mixins.json (20)
+[12mai2026 16:10:25.601] [main/WARN] [mixin/]: Error loading class: org/vivecraft/client_vr/provider/VRRenderer (java.lang.ClassNotFoundException: org.vivecraft.client_vr.provider.VRRenderer)
+[12mai2026 16:10:25.601] [main/DEBUG] [mixin/]: Skipping virtual target org.vivecraft.client_vr.provider.VRRenderer for simpleclouds.mixins.json:vivecraft.MixinVRRenderer
+[12mai2026 16:10:25.601] [main/DEBUG] [mixin/]: Preparing architectury.mixins.json (9)
+[12mai2026 16:10:25.608] [main/DEBUG] [mixin/]: Preparing architectury-common.mixins.json (10)
+[12mai2026 16:10:25.612] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/item/BucketItem
+[12mai2026 16:10:25.618] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/EntityType
+[12mai2026 16:10:25.786] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/block/LiquidBlock
+[12mai2026 16:10:25.791] [main/DEBUG] [mixin/]: Preparing DistantHorizons.forge.mixins.json (12)
+[12mai2026 16:10:25.831] [main/DEBUG] [mixin/]: Preparing lithostitched.mixins.json (18)
+[12mai2026 16:10:25.843] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate
+[12mai2026 16:10:25.901] [main/DEBUG] [mixin/]: Preparing lithostitched.forge.mixins.json (2)
+[12mai2026 16:10:25.904] [main/DEBUG] [mixin/]: Preparing gaboulibs.mixins.json (0)
+[12mai2026 16:10:25.904] [main/DEBUG] [mixin/]: Preparing embeddium.mixins.json (0)
+[12mai2026 16:10:25.904] [main/DEBUG] [mixin/]: Preparing chloride.mixin.json (32)
+[12mai2026 16:10:25.926] [main/WARN] [mixin/]: Error loading class: dev/emi/emi/screen/EmiScreenManager (java.lang.ClassNotFoundException: dev.emi.emi.screen.EmiScreenManager)
+[12mai2026 16:10:25.927] [main/DEBUG] [mixin/]: Skipping virtual target dev.emi.emi.screen.EmiScreenManager for chloride.mixin.json:jei_rei_emi.EmiOverlayMixin
+[12mai2026 16:10:25.929] [main/WARN] [mixin/]: Error loading class: me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl (java.lang.ClassNotFoundException: me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl)
+[12mai2026 16:10:25.930] [main/DEBUG] [mixin/]: Skipping virtual target me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl for chloride.mixin.json:jei_rei_emi.ReiOverlayMixin
+[12mai2026 16:10:25.931] [main/DEBUG] [mixin/]: Preparing sereneseasonsplus.mixins.json (8)
+[12mai2026 16:10:25.942] [main/DEBUG] [mixin/]: Preparing tectonic.mixins.json (13)
+[12mai2026 16:10:25.954] [main/DEBUG] [mixin/]: Preparing tectonic_1.20.1.mixins.json (3)
+[12mai2026 16:10:25.981] [main/DEBUG] [mixin/]: Inner class glitchcore/forge/mixin/impl/MixinPacketHandler$1 in glitchcore/forge/mixin/impl/MixinPacketHandler on glitchcore/network/PacketHandler gets unique name glitchcore/network/PacketHandler$Anonymous$267cb16fd0234a6bb2a36c78471b8ab9
+[12mai2026 16:10:26.099] [main/DEBUG] [mixin/]: Prepared 168 mixins in 1,426 sec (8,5ms avg) (0ms load, 0ms transform, 0ms plugin)
+[12mai2026 16:10:26.203] [main/DEBUG] [io.netty.util.internal.logging.InternalLoggerFactory/]: Using SLF4J as the default logging framework
+[12mai2026 16:10:26.211] [main/DEBUG] [io.netty.util.ResourceLeakDetector/]: -Dio.netty.leakDetection.level: simple
+[12mai2026 16:10:26.211] [main/DEBUG] [io.netty.util.ResourceLeakDetector/]: -Dio.netty.leakDetection.targetRecords: 4
+[12mai2026 16:10:26.358] [main/INFO] [net.minecraftforge.data.loading.DatagenModLoader/]: Initializing Data Gatherer for mods [projectatmosphere]
+[12mai2026 16:10:26.370] [main/INFO] [MixinExtras|Service/]: Initializing MixinExtras via com.llamalad7.mixinextras.service.MixinExtrasServiceImpl(version=0.3.5).
+[12mai2026 16:10:26.371] [main/DEBUG] [mixin/]: Registering new injector for @SugarWrapper with com.llamalad7.mixinextras.sugar.impl.SugarWrapperInjectionInfo
+[12mai2026 16:10:26.371] [main/DEBUG] [mixin/]: Registering new injector for @FactoryRedirectWrapper with com.llamalad7.mixinextras.wrapper.factory.FactoryRedirectWrapperInjectionInfo
+[12mai2026 16:10:26.375] [main/DEBUG] [mixin/]: Mixing common.MappedRegistryAccessor from lithostitched.mixins.json into net.minecraft.core.MappedRegistry
+[12mai2026 16:10:26.424] [main/DEBUG] [mixin/]: Mixing server.MixinUtilBackgroundThread from DistantHorizons.forge.mixins.json into net.minecraft.Util
+[12mai2026 16:10:26.534] [main/DEBUG] [mixin/]: Mixing inject.MixinGameEvent from architectury-common.mixins.json into net.minecraft.world.level.gameevent.GameEvent
+[12mai2026 16:10:26.544] [main/DEBUG] [mixin/]: Mixing common.HolderReferenceAccessor from lithostitched.mixins.json into net.minecraft.core.Holder$Reference
+[12mai2026 16:10:26.567] [main/DEBUG] [mixin/]: Mixing inject.MixinBlock from architectury-common.mixins.json into net.minecraft.world.level.block.Block
+[12mai2026 16:10:26.587] [main/DEBUG] [mixin/]: Mixing MixinBlockStateBase from sereneseasons.mixins.json into net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase
+[12mai2026 16:10:26.600] [main/DEBUG] [mixin/]: Mixing server.MixinEntity from DistantHorizons.forge.mixins.json into net.minecraft.world.entity.Entity
+[12mai2026 16:10:26.652] [main/DEBUG] [mixin/]: Mixing MixinLevel from sereneseasons.mixins.json into net.minecraft.world.level.Level
+[12mai2026 16:10:26.654] [main/DEBUG] [mixin/]: Mixing MixinLevel from simpleclouds.mixins.json into net.minecraft.world.level.Level
+[12mai2026 16:10:26.675] [main/DEBUG] [mixin/]: Mixing ServerLevelSnowStormMixin from projectatmosphere.mixins.json into net.minecraft.server.level.ServerLevel
+[12mai2026 16:10:26.677] [main/DEBUG] [mixin/]: Mixing MixinServerLevel from glitchcore.mixins.json into net.minecraft.server.level.ServerLevel
+[12mai2026 16:10:26.678] [main/DEBUG] [mixin/]: Mixing MixinServerLevel from sereneseasons.mixins.json into net.minecraft.server.level.ServerLevel
+[12mai2026 16:10:26.678] [main/DEBUG] [mixin/]: Mixing MixinServerLevel from simpleclouds.mixins.json into net.minecraft.server.level.ServerLevel
+[12mai2026 16:10:26.679] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$createCloudManager_init$1()Ldev/nonamecrackers2/simpleclouds/common/world/CloudData; to mdb27d7e$lambda$simpleclouds$createCloudManager_init$1$0 in simpleclouds.mixins.json:MixinServerLevel
+[12mai2026 16:10:26.680] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$createCloudManager_init$0(Lnet/minecraft/nbt/CompoundTag;)Ldev/nonamecrackers2/simpleclouds/common/world/CloudData; to mdb27d7e$lambda$simpleclouds$createCloudManager_init$0$1 in simpleclouds.mixins.json:MixinServerLevel
+[12mai2026 16:10:26.685] [main/DEBUG] [mixin/]: Mixing MixinServerLevelAccessor from simpleclouds.mixins.json into net.minecraft.server.level.ServerLevel
+[12mai2026 16:10:26.687] [main/DEBUG] [mixin/]: Mixing ServerLevelMixin from sereneseasonsplus.mixins.json into net.minecraft.server.level.ServerLevel
+[12mai2026 16:10:26.801] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/biome/Biome
+[12mai2026 16:10:26.868] [main/DEBUG] [mixin/]: Mixing inject.MixinFluid from architectury-common.mixins.json into net.minecraft.world.level.material.Fluid
+[12mai2026 16:10:26.877] [main/DEBUG] [mixin/]: Mixing inject.MixinItem from architectury-common.mixins.json into net.minecraft.world.item.Item
+[12mai2026 16:10:26.901] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/EntityType
+[12mai2026 16:10:26.917] [main/DEBUG] [mixin/]: Mixing inject.MixinEntityType from architectury-common.mixins.json into net.minecraft.world.entity.EntityType
+[12mai2026 16:10:26.918] [main/DEBUG] [mixin/]: Mixing EntityDistanceCullingMixin$EntityTypeMixin from chloride.mixin.json into net.minecraft.world.entity.EntityType
+[12mai2026 16:10:26.955] [main/DEBUG] [mixin/]: Mixing ParticlesMixins$ParticleTypeMixin from chloride.mixin.json into net.minecraft.core.particles.ParticleType
+[12mai2026 16:10:26.959] [main/DEBUG] [mixin/]: Mixing MixinBlockEntityType from crackerslib.mixins.json into net.minecraft.world.level.block.entity.BlockEntityType
+[12mai2026 16:10:26.961] [main/DEBUG] [mixin/]: Mixing EntityDistanceCullingMixin$TileEntityTypeMixin from chloride.mixin.json into net.minecraft.world.level.block.entity.BlockEntityType
+[12mai2026 16:10:27.009] [main/DEBUG] [mixin/]: Mixing WorldCarverMixin from tectonic.mixins.json into net.minecraft.world.level.levelgen.carver.WorldCarver
+[12mai2026 16:10:27.087] [main/DEBUG] [mixin/]: Mixing features.render.immediate.DirectionMixin from embeddium.mixins.json into net.minecraft.core.Direction
+[12mai2026 16:10:27.211] [main/DEBUG] [mixin/]: Mixing SpreadingSnowyDirtBlockMixin from sereneseasonsplus.mixins.json into net.minecraft.world.level.block.SpreadingSnowyDirtBlock
+[12mai2026 16:10:27.243] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/block/LiquidBlock
+[12mai2026 16:10:27.243] [main/DEBUG] [mixin/]: Mixing inject.MixinLiquidBlock from architectury-common.mixins.json into net.minecraft.world.level.block.LiquidBlock
+[12mai2026 16:10:27.252] [main/DEBUG] [mixin/]: Mixing leaves_culling.LeavesBlockMixin from chloride.mixin.json into net.minecraft.world.level.block.LeavesBlock
+[12mai2026 16:10:27.254] [main/DEBUG] [mixin/]: Mixing features.options.render_layers.LeavesBlockMixin from embeddium.mixins.json into net.minecraft.world.level.block.LeavesBlock
+[12mai2026 16:10:27.264] [main/WARN] [mixin/]: Method overwrite conflict for skipRendering in embeddium.mixins.json:features.options.render_layers.LeavesBlockMixin, previously written by me.srrapero720.chloride.mixins.impl.leaves_culling.LeavesBlockMixin. Skipping method.
+[12mai2026 16:10:27.266] [main/DEBUG] [mixin/]: Unexpected: Registered method skipRendering(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;)Z in net.minecraft.world.level.block.LeavesBlock was not merged
+[12mai2026 16:10:27.274] [main/DEBUG] [mixin/]: Mixing FastBlocksMixins$BedMixin from chloride.mixin.json into net.minecraft.world.level.block.BedBlock
+[12mai2026 16:10:27.298] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/block/StairBlock
+[12mai2026 16:10:27.302] [main/DEBUG] [mixin/]: Mixing FastBlocksMixins$ChestsMixin from chloride.mixin.json into net.minecraft.world.level.block.ChestBlock
+[12mai2026 16:10:27.352] [main/DEBUG] [mixin/]: Mixing FastBlocksMixins$ChestsMixin from chloride.mixin.json into net.minecraft.world.level.block.EnderChestBlock
+[12mai2026 16:10:27.357] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/block/FlowerPotBlock
+[12mai2026 16:10:27.511] [main/DEBUG] [mixin/]: Mixing MixinFallingBlockEntity from architectury.mixins.json into net.minecraft.world.entity.item.FallingBlockEntity
+[12mai2026 16:10:28.092] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/item/ItemStack
+[12mai2026 16:10:28.093] [main/DEBUG] [mixin/]: Mixing MixinItemStack from glitchcore.mixins.json into net.minecraft.world.item.ItemStack
+[12mai2026 16:10:28.584] [main/DEBUG] [mixin/]: Mixing MixinLightningBolt from architectury-common.mixins.json into net.minecraft.world.entity.LightningBolt
+[12mai2026 16:10:28.634] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/animal/frog/Tadpole
+[12mai2026 16:10:28.683] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/item/BucketItem
+[12mai2026 16:10:28.683] [main/DEBUG] [mixin/]: Mixing inject.MixinBucketItem from architectury-common.mixins.json into net.minecraft.world.item.BucketItem
+[12mai2026 16:10:28.765] [main/DEBUG] [mixin/]: Mixing inject.MixinItemProperties from architectury-common.mixins.json into net.minecraft.world.item.Item$Properties
+[12mai2026 16:10:29.423] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/Spider
+[12mai2026 16:10:29.452] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/Zombie
+[12mai2026 16:10:29.468] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/ZombieVillager
+[12mai2026 16:10:29.481] [main/DEBUG] [mixin/]: Mixing vanillacompat.MixinThrownTrident from simpleclouds.mixins.json into net.minecraft.world.entity.projectile.ThrownTrident
+[12mai2026 16:10:29.514] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/Evoker$EvokerSummonSpellGoal
+[12mai2026 16:10:29.601] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/animal/horse/SkeletonTrapGoal
+[12mai2026 16:10:29.610] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/monster/Strider
+[12mai2026 16:10:29.648] [main/DEBUG] [mixin/]: Mixing MixinServerPlayer from glitchcore.forge.mixins.json into net.minecraft.server.level.ServerPlayer
+[12mai2026 16:10:29.649] [main/DEBUG] [mixin/]: Mixing server.MixinServerPlayer from DistantHorizons.forge.mixins.json into net.minecraft.server.level.ServerPlayer
+[12mai2026 16:10:29.679] [main/DEBUG] [mixin/]: Mixing ChunkAccessMixin from tectonic.mixins.json into net.minecraft.world.level.chunk.ChunkAccess
+[12mai2026 16:10:29.686] [main/DEBUG] [mixin/]: Mixing LevelChunkSnowMixin from sereneseasonsplus.mixins.json into net.minecraft.world.level.chunk.LevelChunk
+[12mai2026 16:10:29.717] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/entity/npc/Villager
+[12mai2026 16:10:29.783] [main/DEBUG] [mixin/]: Mixing inject.MixinFoodPropertiesBuilder from architectury-common.mixins.json into net.minecraft.world.food.FoodProperties$Builder
+[12mai2026 16:10:29.785] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/effect/MobEffectInstance
+[12mai2026 16:10:29.957] [main/DEBUG] [mixin/]: Mixing HeightmapMixin from tectonic.mixins.json into net.minecraft.world.level.levelgen.Heightmap
+[12mai2026 16:10:29.963] [main/DEBUG] [mixin/]: Mixing server.MixinChunkGenerator from DistantHorizons.forge.mixins.json into net.minecraft.world.level.chunk.ChunkGenerator
+[12mai2026 16:10:29.963] [main/DEBUG] [mixin/]: Mixing server.MixinTFChunkGenerator from DistantHorizons.forge.mixins.json into net.minecraft.world.level.chunk.ChunkGenerator
+[12mai2026 16:10:30.106] [main/DEBUG] [mixin/]: Mixing darkness.DimensionTypeMixin from chloride.mixin.json into net.minecraft.world.level.dimension.DimensionType
+[12mai2026 16:10:30.106] [main/DEBUG] [mixin/]: Mixing DimensionTypeAccessor from tectonic.mixins.json into net.minecraft.world.level.dimension.DimensionType
+[12mai2026 16:10:30.168] [main/DEBUG] [mixin/]: Mixing common.PlacedFeatureAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.placement.PlacedFeature
+[12mai2026 16:10:30.179] [main/DEBUG] [mixin/]: Mixing common.StructureProcessorListAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList
+[12mai2026 16:10:30.233] [main/DEBUG] [mixin/]: Mixing common.StructureSetAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.StructureSet
+[12mai2026 16:10:30.238] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/Structure
+[12mai2026 16:10:30.246] [main/DEBUG] [mixin/]: Mixing StructurePieceMixin from tectonic.mixins.json into net.minecraft.world.level.levelgen.structure.StructurePiece
+[12mai2026 16:10:30.269] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces$OceanRuinPiece
+[12mai2026 16:10:30.274] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece
+[12mai2026 16:10:30.279] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces$OceanMonumentPiece
+[12mai2026 16:10:30.287] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces$WoodlandMansionPiece
+[12mai2026 16:10:30.291] [main/DEBUG] [mixin/]: Mixing common.ShipwreckPieceMixin from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.structures.ShipwreckPieces$ShipwreckPiece
+[12mai2026 16:10:30.305] [main/DEBUG] [mixin/]: Mixing common.JigsawStructureMixin from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.structures.JigsawStructure
+[12mai2026 16:10:30.306] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/levelgen/structure/Structure
+[12mai2026 16:10:30.313] [main/DEBUG] [mixin/]: Mixing common.StructureTemplatePoolAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool
+[12mai2026 16:10:30.317] [main/DEBUG] [mixin/]: Mixing common.StructureTemplatePoolMixin from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool
+[12mai2026 16:10:30.318] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$compileRawTemplates$0(Lcom/mojang/datafixers/util/Pair;)V to mdb27d7e$lambda$compileRawTemplates$0$0 in lithostitched.mixins.json:common.StructureTemplatePoolMixin
+[12mai2026 16:10:30.363] [main/DEBUG] [net.minecraftforge.coremod.transformer.CoreModBaseTransformer/COREMOD]: Transforming net/minecraft/world/level/biome/Biome
+[12mai2026 16:10:30.363] [main/DEBUG] [mixin/]: Mixing features.world.biome.BiomeMixin from embeddium.mixins.json into net.minecraft.world.level.biome.Biome
+[12mai2026 16:10:30.364] [main/DEBUG] [mixin/]: Mixing BiomeFreezingMixin from projectatmosphere.mixins.json into net.minecraft.world.level.biome.Biome
+[12mai2026 16:10:30.364] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$resolveTemperature$1(Lnet/minecraft/resources/ResourceKey;)Lnet/minecraft/resources/ResourceLocation; to mdb27d7e$lambda$resolveTemperature$1$0 in projectatmosphere.mixins.json:BiomeFreezingMixin
+[12mai2026 16:10:30.365] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$resolveTemperature$0(Lnet/minecraft/resources/ResourceKey;)Lnet/minecraft/resources/ResourceLocation; to mdb27d7e$lambda$resolveTemperature$0$1 in projectatmosphere.mixins.json:BiomeFreezingMixin
+[12mai2026 16:10:30.369] [main/DEBUG] [mixin/]: Mixing MixinBiome from sereneseasons.mixins.json into net.minecraft.world.level.biome.Biome
+[12mai2026 16:10:30.369] [main/DEBUG] [mixin/]: Mixing client.MixinBiomeClient from sereneseasons.mixins.json into net.minecraft.world.level.biome.Biome
+[12mai2026 16:10:30.369] [main/DEBUG] [mixin/]: Mixing BiomeMixin from tectonic.mixins.json into net.minecraft.world.level.biome.Biome
+[12mai2026 16:10:30.389] [main/DEBUG] [mixin/]: Mixing features.render.world.ClientLevelMixin from embeddium.mixins.json into net.minecraft.client.multiplayer.ClientLevel
+[12mai2026 16:10:30.389] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$new$0(Lnet/minecraft/world/level/biome/AmbientParticleSettings;)V to mdb27d7e$lambda$new$0$0 in embeddium.mixins.json:features.render.world.ClientLevelMixin
+[12mai2026 16:10:30.391] [main/DEBUG] [mixin/]: Mixing MixinClientLevel from simpleclouds.mixins.json into net.minecraft.client.multiplayer.ClientLevel
+[12mai2026 16:10:30.393] [main/DEBUG] [mixin/]: Mixing MixinClientLevel from architectury.mixins.json into net.minecraft.client.multiplayer.ClientLevel
+[12mai2026 16:10:30.393] [main/DEBUG] [mixin/]: Mixing core.world.biome.ClientWorldMixin from embeddium.mixins.json into net.minecraft.client.multiplayer.ClientLevel
+[12mai2026 16:10:30.393] [main/DEBUG] [mixin/]: Mixing core.world.map.ClientWorldMixin from embeddium.mixins.json into net.minecraft.client.multiplayer.ClientLevel
+[12mai2026 16:10:30.395] [main/DEBUG] [mixin/]: Mixing features.render.world.sky.ClientWorldMixin from embeddium.mixins.json into net.minecraft.client.multiplayer.ClientLevel
+[12mai2026 16:10:30.395] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$redirectSampleColor$0(Lnet/minecraft/world/level/Level;III)I to mdb27d7e$lambda$redirectSampleColor$0$1 in embeddium.mixins.json:features.render.world.sky.ClientWorldMixin
+[12mai2026 16:10:30.434] [main/DEBUG] [mixin/]: Mixing TemperatureModifierMixin from tectonic.mixins.json into net.minecraft.world.level.biome.Biome$TemperatureModifier$2
+[12mai2026 16:10:30.480] [main/DEBUG] [mixin/]: Mixing NoiseBasedChunkGeneratorMixin from tectonic.mixins.json into net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator
+[12mai2026 16:10:30.480] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$tectonic$fixLavaLevel$2(Lnet/minecraft/core/Holder;)Lnet/minecraft/world/level/levelgen/Aquifer$FluidPicker; to mdb27d7e$lambda$tectonic$fixLavaLevel$2$0 in tectonic.mixins.json:NoiseBasedChunkGeneratorMixin
+[12mai2026 16:10:30.480] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$tectonic$fixLavaLevel$1(Lnet/minecraft/world/level/levelgen/Aquifer$FluidStatus;IILnet/minecraft/world/level/levelgen/Aquifer$FluidStatus;Lnet/minecraft/world/level/levelgen/Aquifer$FluidStatus;III)Lnet/minecraft/world/level/levelgen/Aquifer$FluidStatus; to mdb27d7e$lambda$tectonic$fixLavaLevel$1$1 in tectonic.mixins.json:NoiseBasedChunkGeneratorMixin
+[12mai2026 16:10:30.480] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$tectonic$fixLavaLevel$0(Lnet/minecraft/resources/ResourceKey;)Ljava/lang/Boolean; to mdb27d7e$lambda$tectonic$fixLavaLevel$0$2 in tectonic.mixins.json:NoiseBasedChunkGeneratorMixin
+[12mai2026 16:10:30.486] [main/DEBUG] [mixin/]: Mixing common.NoiseGeneratorSettingsAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.NoiseGeneratorSettings
+[12mai2026 16:10:30.490] [main/DEBUG] [mixin/]: Mixing NoiseSettingsAccessor from tectonic.mixins.json into net.minecraft.world.level.levelgen.NoiseSettings
+[12mai2026 16:10:30.544] [main/DEBUG] [mixin/]: Mixing common.SinglePoolElementAccessor from lithostitched.mixins.json into net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement
+[12mai2026 16:10:30.589] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Creating vanilla freeze snapshot
+[12mai2026 16:10:30.593] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:block Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.599] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:fluid Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.599] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:item Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.604] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:mob_effect Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.604] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:sound_event Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.608] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:potion Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.608] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:enchantment Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.608] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:entity_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.609] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:block_entity_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.609] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:particle_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.610] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:menu Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.610] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:painting_variant Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.610] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:recipe_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.610] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:recipe_serializer Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.610] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:attribute Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.610] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:stat_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.610] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:command_argument_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.610] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:villager_profession Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.610] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:point_of_interest_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.611] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:memory_module_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.612] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:sensor_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.612] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:schedule Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.612] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:activity Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.612] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/carver Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.612] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/feature Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.613] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:chunk_status Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.613] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/block_state_provider_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.613] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/foliage_placer_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.613] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/tree_decorator_type Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.613] [main/DEBUG] [net.minecraftforge.registries.ForgeRegistry/REGISTRIES]: Registry minecraft:worldgen/biome Sync: VANILLA -> ACTIVE
+[12mai2026 16:10:30.616] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Vanilla freeze snapshot created
+[12mai2026 16:10:30.652] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: -Dio.netty.noUnsafe: false
+[12mai2026 16:10:30.652] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: Java version: 17
+[12mai2026 16:10:30.653] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: sun.misc.Unsafe.theUnsafe: available
+[12mai2026 16:10:30.653] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: sun.misc.Unsafe.copyMemory: available
+[12mai2026 16:10:30.653] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: sun.misc.Unsafe.storeFence: available
+[12mai2026 16:10:30.653] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: java.nio.Buffer.address: available
+[12mai2026 16:10:30.654] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: direct buffer constructor: unavailable
java.lang.UnsupportedOperationException: Reflective setAccessible(true) disabled
at io.netty.util.internal.ReflectionUtil.trySetAccessible(ReflectionUtil.java:31) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
at io.netty.util.internal.PlatformDependent0$5.run(PlatformDependent0.java:288) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
@@ -824,19 +814,19 @@ java.lang.UnsupportedOperationException: Reflective setAccessible(true) disabled
at io.netty.util.ConstantPool.(ConstantPool.java:34) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
at io.netty.util.AttributeKey$1.(AttributeKey.java:27) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
at io.netty.util.AttributeKey.(AttributeKey.java:27) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
- at net.minecraftforge.network.NetworkConstants.(NetworkConstants.java:34) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
- at net.minecraftforge.network.NetworkHooks.init(NetworkHooks.java:52) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
- at net.minecraft.server.Bootstrap.bootStrap(Bootstrap.java:62) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
- at net.minecraftforge.data.loading.DatagenModLoader.begin(DatagenModLoader.java:42) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
- at net.minecraft.data.Main.main(Main.java:90) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraftforge.network.NetworkConstants.(NetworkConstants.java:34) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraftforge.network.NetworkHooks.init(NetworkHooks.java:52) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraft.server.Bootstrap.bootStrap(Bootstrap.java:62) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraftforge.data.loading.DatagenModLoader.begin(DatagenModLoader.java:42) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraft.data.Main.main(Main.java:90) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:569) ~[?:?]
- at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.runTarget(CommonLaunchHandler.java:111) ~[fmlloader-1.20.1-47.4.9.jar:?]
- at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.dataService(CommonLaunchHandler.java:107) ~[fmlloader-1.20.1-47.4.9.jar:?]
- at net.minecraftforge.fml.loading.targets.ForgeDataUserdevLaunchHandler.devService(ForgeDataUserdevLaunchHandler.java:22) ~[fmlloader-1.20.1-47.4.9.jar:?]
- at net.minecraftforge.fml.loading.targets.CommonDevLaunchHandler.lambda$makeService$7(CommonDevLaunchHandler.java:135) ~[fmlloader-1.20.1-47.4.9.jar:?]
+ at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.runTarget(CommonLaunchHandler.java:111) ~[fmlloader-1.20.1-47.4.13.jar:?]
+ at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.dataService(CommonLaunchHandler.java:107) ~[fmlloader-1.20.1-47.4.13.jar:?]
+ at net.minecraftforge.fml.loading.targets.ForgeDataUserdevLaunchHandler.devService(ForgeDataUserdevLaunchHandler.java:22) ~[fmlloader-1.20.1-47.4.13.jar:?]
+ at net.minecraftforge.fml.loading.targets.CommonDevLaunchHandler.lambda$makeService$7(CommonDevLaunchHandler.java:135) ~[fmlloader-1.20.1-47.4.13.jar:?]
at cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:30) ~[modlauncher-10.0.9.jar:?]
at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:53) ~[modlauncher-10.0.9.jar:?]
at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:71) ~[modlauncher-10.0.9.jar:?]
@@ -845,8 +835,8 @@ java.lang.UnsupportedOperationException: Reflective setAccessible(true) disabled
at cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26) ~[modlauncher-10.0.9.jar:?]
at cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23) ~[modlauncher-10.0.9.jar:?]
at cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:141) ~[bootstraplauncher-1.1.2.jar:?]
-[24nov.2025 23:48:53.953] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: java.nio.Bits.unaligned: available, true
-[24nov.2025 23:48:53.953] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable
+[12mai2026 16:10:30.666] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: java.nio.Bits.unaligned: available, true
+[12mai2026 16:10:30.667] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable
java.lang.IllegalAccessException: class io.netty.util.internal.PlatformDependent0$7 (in module io.netty.common) cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to module io.netty.common
at jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392) ~[?:?]
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674) ~[?:?]
@@ -859,19 +849,19 @@ java.lang.IllegalAccessException: class io.netty.util.internal.PlatformDependent
at io.netty.util.ConstantPool.(ConstantPool.java:34) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
at io.netty.util.AttributeKey$1.(AttributeKey.java:27) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
at io.netty.util.AttributeKey.(AttributeKey.java:27) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
- at net.minecraftforge.network.NetworkConstants.(NetworkConstants.java:34) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
- at net.minecraftforge.network.NetworkHooks.init(NetworkHooks.java:52) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
- at net.minecraft.server.Bootstrap.bootStrap(Bootstrap.java:62) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
- at net.minecraftforge.data.loading.DatagenModLoader.begin(DatagenModLoader.java:42) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
- at net.minecraft.data.Main.main(Main.java:90) ~[forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraftforge.network.NetworkConstants.(NetworkConstants.java:34) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraftforge.network.NetworkHooks.init(NetworkHooks.java:52) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraft.server.Bootstrap.bootStrap(Bootstrap.java:62) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraftforge.data.loading.DatagenModLoader.begin(DatagenModLoader.java:42) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
+ at net.minecraft.data.Main.main(Main.java:90) ~[forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:569) ~[?:?]
- at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.runTarget(CommonLaunchHandler.java:111) ~[fmlloader-1.20.1-47.4.9.jar:?]
- at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.dataService(CommonLaunchHandler.java:107) ~[fmlloader-1.20.1-47.4.9.jar:?]
- at net.minecraftforge.fml.loading.targets.ForgeDataUserdevLaunchHandler.devService(ForgeDataUserdevLaunchHandler.java:22) ~[fmlloader-1.20.1-47.4.9.jar:?]
- at net.minecraftforge.fml.loading.targets.CommonDevLaunchHandler.lambda$makeService$7(CommonDevLaunchHandler.java:135) ~[fmlloader-1.20.1-47.4.9.jar:?]
+ at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.runTarget(CommonLaunchHandler.java:111) ~[fmlloader-1.20.1-47.4.13.jar:?]
+ at net.minecraftforge.fml.loading.targets.CommonLaunchHandler.dataService(CommonLaunchHandler.java:107) ~[fmlloader-1.20.1-47.4.13.jar:?]
+ at net.minecraftforge.fml.loading.targets.ForgeDataUserdevLaunchHandler.devService(ForgeDataUserdevLaunchHandler.java:22) ~[fmlloader-1.20.1-47.4.13.jar:?]
+ at net.minecraftforge.fml.loading.targets.CommonDevLaunchHandler.lambda$makeService$7(CommonDevLaunchHandler.java:135) ~[fmlloader-1.20.1-47.4.13.jar:?]
at cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:30) ~[modlauncher-10.0.9.jar:?]
at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:53) ~[modlauncher-10.0.9.jar:?]
at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:71) ~[modlauncher-10.0.9.jar:?]
@@ -880,222 +870,196 @@ java.lang.IllegalAccessException: class io.netty.util.internal.PlatformDependent
at cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26) ~[modlauncher-10.0.9.jar:?]
at cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23) ~[modlauncher-10.0.9.jar:?]
at cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:141) ~[bootstraplauncher-1.1.2.jar:?]
-[24nov.2025 23:48:53.954] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: java.nio.DirectByteBuffer.(long, int): unavailable
-[24nov.2025 23:48:53.954] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: sun.misc.Unsafe: available
-[24nov.2025 23:48:53.954] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: maxDirectMemory: 17112760320 bytes (maybe)
-[24nov.2025 23:48:53.955] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.tmpdir: C:\Users\matga\AppData\Local\Temp (java.io.tmpdir)
-[24nov.2025 23:48:53.955] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.bitMode: 64 (sun.arch.data.model)
-[24nov.2025 23:48:53.955] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: Platform: Windows
-[24nov.2025 23:48:53.955] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.maxDirectMemory: -1 bytes
-[24nov.2025 23:48:53.955] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.uninitializedArrayAllocationThreshold: -1
-[24nov.2025 23:48:53.956] [main/DEBUG] [io.netty.util.internal.CleanerJava9/]: java.nio.ByteBuffer.cleaner(): available
-[24nov.2025 23:48:53.956] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.noPreferDirect: false
-[24nov.2025 23:48:53.986] [main/DEBUG] [net.minecraftforge.network.NetworkHooks/]: Loading Network data for FML net version: FML3
-[24nov.2025 23:48:53.995] [main/DEBUG] [net.minecraftforge.fml.ModWorkManager/LOADING]: Using 24 threads for parallel mod-loading
-[24nov.2025 23:48:53.997] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:53.998] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for betterdays.BetterDaysForge
-[24nov.2025 23:48:53.999] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:53.999] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for snownee.jade.util.CommonProxy
-[24nov.2025 23:48:54.000] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.000] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for mezz.jei.forge.JustEnoughItems
-[24nov.2025 23:48:54.000] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.000] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for dev.worldgen.lithostitched.LithostitchedForge
-[24nov.2025 23:48:54.001] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.001] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.lucko.spark.forge.ForgeSparkMod
-[24nov.2025 23:48:54.001] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.001] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for com.Gabou.sereneseasonsplus.SereneSeasonsPlusForge
-[24nov.2025 23:48:54.002] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.002] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for nonamecrackers2.crackerslib.CrackersLib
-[24nov.2025 23:48:54.002] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.002] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for com.llamalad7.mixinextras.platform.forge.MixinExtrasMod
-[24nov.2025 23:48:54.002] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.003] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for glitchcore.forge.GlitchCoreForge
-[24nov.2025 23:48:54.003] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.003] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for sereneseasons.forge.core.SereneSeasonsForge
-[24nov.2025 23:48:54.004] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.004] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for dev.nonamecrackers2.simpleclouds.SimpleCloudsMod
-[24nov.2025 23:48:54.004] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.004] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for net.Gabou.projectatmosphere.ProjectAtmosphere
-[24nov.2025 23:48:54.005] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.005] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for dev.architectury.forge.ArchitecturyForge
-[24nov.2025 23:48:54.005] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.005] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for technology.roughness.whitenoise.WhiteNoiseForge
-[24nov.2025 23:48:54.006] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.006] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.shedaniel.clothconfig.ClothConfigForge
-[24nov.2025 23:48:54.006] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.006] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for net.Gabou.gaboulibs.forge.GaboulibsForge
-[24nov.2025 23:48:54.006] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.006] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for net.minecraftforge.common.ForgeMod
-[24nov.2025 23:48:54.007] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.007] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for auroras.Auroras
-[24nov.2025 23:48:54.007] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.007] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for rainbows.Rainbows
-[24nov.2025 23:48:54.007] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.007] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.jellysquid.mods.sodium.client.SodiumClientMod
-[24nov.2025 23:48:54.008] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.008] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.jellysquid.mods.sodium.client.RubidiumStub
-[24nov.2025 23:48:54.008] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.008] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.srrapero720.chloride.Chloride
-[24nov.2025 23:48:54.009] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@753fd7a1 - got cpw.mods.cl.ModuleClassLoader@398474a2
-[24nov.2025 23:48:54.009] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for dev.worldgen.tectonic.TectonicLexforge
-[24nov.2025 23:48:54.017] [modloading-worker-0/INFO] [projectatmosphere/]: Project Atmosphere is loading!
-[24nov.2025 23:48:54.019] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for mixinextras
-[24nov.2025 23:48:54.019] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for glitchcore
-[24nov.2025 23:48:54.024] [modloading-worker-0/DEBUG] [Better Days/]: Loaded betterdays.platform.ForgeClientPlatform@7eeadf10 for service interface betterdays.platform.services.IClientPlatform
-[24nov.2025 23:48:54.024] [modloading-worker-0/INFO] [chloride/Main]: Chloride is here, lets make your experience taste-able
-[24nov.2025 23:48:54.024] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for chloride
-[24nov.2025 23:48:54.024] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for rubidium
-[24nov.2025 23:48:54.027] [modloading-worker-0/DEBUG] [Better Days/]: Loaded betterdays.platform.ForgePlatform@3ecc3959 for service interface betterdays.platform.services.IPlatform
-[24nov.2025 23:48:54.028] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for cloth_config
-[24nov.2025 23:48:54.028] [modloading-worker-0/DEBUG] [mixin/]: Mixing MinecraftServerMixin from sereneseasonsplus.mixins.json into net.minecraft.server.MinecraftServer
-[24nov.2025 23:48:54.029] [modloading-worker-0/DEBUG] [Better Days/]: Loaded betterdays.platform.ForgeRegistryProvider@3b3132ef for service interface betterdays.platform.services.IRegistryFactory
-[24nov.2025 23:48:54.034] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinLevelEvent from architectury.mixins.json into net.minecraftforge.event.level.LevelEvent
-[24nov.2025 23:48:54.036] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.VillagerTradesEventHandler to FORGE
-[24nov.2025 23:48:54.036] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.sodium.SodiumFeatures to FORGE
-[24nov.2025 23:48:54.043] [modloading-worker-0/DEBUG] [mixin/]: Mixing impl.MixinEnvironment from glitchcore.forge.mixins.json into glitchcore.util.Environment
-[24nov.2025 23:48:54.060] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.TooltipEventHandler to FORGE
-[24nov.2025 23:48:54.067] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.ToolModificationEventHandler to FORGE
-[24nov.2025 23:48:54.068] [modloading-worker-0/DEBUG] [mixin/]: Mixing vivecraft.MixinMinecraftVRMixin from simpleclouds.mixins.json into net.minecraft.client.Minecraft
-[24nov.2025 23:48:54.069] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$onVRStateSwitched_vivecrafft$switchVRSate$0(Ldev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer;)V to mdd47aa8$lambda$simpleclouds$onVRStateSwitched_vivecrafft$switchVRSate$0$0 in simpleclouds.mixins.json:vivecraft.MixinMinecraftVRMixin
-[24nov.2025 23:48:54.069] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.MixinMinecraft from glitchcore.mixins.json into net.minecraft.client.Minecraft
-[24nov.2025 23:48:54.069] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for spark
-[24nov.2025 23:48:54.072] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinMinecraft from simpleclouds.mixins.json into net.minecraft.client.Minecraft
-[24nov.2025 23:48:54.072] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$onClientLevelChange_setLevel$1(Lnet/minecraft/client/multiplayer/ClientLevel;Ldev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer;)V to mdd47aa8$lambda$simpleclouds$onClientLevelChange_setLevel$1$1 in simpleclouds.mixins.json:MixinMinecraft
-[24nov.2025 23:48:54.073] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$appendCrashReportDetails_fillReport$0(Lnet/minecraft/CrashReport;Ldev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer;)V to mdd47aa8$lambda$simpleclouds$appendCrashReportDetails_fillReport$0$2 in simpleclouds.mixins.json:MixinMinecraft
-[24nov.2025 23:48:54.077] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinMinecraft from architectury.mixins.json into net.minecraft.client.Minecraft
-[24nov.2025 23:48:54.077] [modloading-worker-0/DEBUG] [mixin/]: Mixing MinecraftMixin from chloride.mixin.json into net.minecraft.client.Minecraft
-[24nov.2025 23:48:54.078] [modloading-worker-0/DEBUG] [mixin/]: Mixing OverlayMixin from chloride.mixin.json into net.minecraft.client.Minecraft
-[24nov.2025 23:48:54.078] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file crackerslib-client.toml for crackerslib tracking
-[24nov.2025 23:48:54.079] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.render.MinecraftAccessor from embeddium.mixins.json into net.minecraft.client.Minecraft
-[24nov.2025 23:48:54.080] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.MinecraftClientMixin from embeddium.mixins.json into net.minecraft.client.Minecraft
-[24nov.2025 23:48:54.080] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$postInit$0(Lnet/minecraft/client/gui/screens/Screen;)Lnet/minecraft/client/gui/screens/Screen; to mdd47aa8$lambda$postInit$0$3 in embeddium.mixins.json:core.MinecraftClientMixin
-[24nov.2025 23:48:54.149] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for gaboulibs
-[24nov.2025 23:48:54.151] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file jei-server.toml for jei tracking
-[24nov.2025 23:48:54.154] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for crackerslib
-[24nov.2025 23:48:54.155] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.TickEventHandler to FORGE
-[24nov.2025 23:48:54.155] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for whitenoise
-[24nov.2025 23:48:54.155] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.Zoom to FORGE
-[24nov.2025 23:48:54.157] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.TagsUpdatedEventHandler to FORGE
-[24nov.2025 23:48:54.158] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file auroras-common.toml for auroras tracking
-[24nov.2025 23:48:54.158] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file projectatmosphere-common.toml for projectatmosphere tracking
-[24nov.2025 23:48:54.161] [modloading-worker-0/DEBUG] [mixin/]: Mixing compat.auroras.AuroraRendererMixin from projectatmosphere.mixins.json into auroras.util.AuroraRenderer
-[24nov.2025 23:48:54.171] [modloading-worker-0/INFO] [projectatmosphere/]: No temperature mod loaded, skipping compatibility setup.
-[24nov.2025 23:48:54.171] [modloading-worker-0/INFO] [projectatmosphere/]: Sand Storms mod not found.
-[24nov.2025 23:48:54.171] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.forge.ForgeVersion/CORE]: Forge Version package package net.minecraftforge.versions.forge, Forge, version 47.4 from cpw.mods.modlauncher.TransformingClassLoader@753fd7a1
-[24nov.2025 23:48:54.172] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.forge.ForgeVersion/CORE]: Found Forge version 47.4.9
-[24nov.2025 23:48:54.172] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.RegistryEventHandler to MOD
-[24nov.2025 23:48:54.172] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.forge.ForgeVersion/CORE]: Found Forge spec 47.4
-[24nov.2025 23:48:54.172] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.forge.ForgeVersion/CORE]: Found Forge group net.minecraftforge
-[24nov.2025 23:48:54.172] [modloading-worker-0/DEBUG] [WhiteNoise/]: Loaded technology.roughness.whitenoise.platform.ForgeConfigHelper@4e8d369a for service interface technology.roughness.whitenoise.platform.services.IConfigHelper
-[24nov.2025 23:48:54.173] [modloading-worker-0/DEBUG] [WhiteNoise/]: Loaded technology.roughness.whitenoise.platform.ForgeClientPlatform@36f106ee for service interface technology.roughness.whitenoise.platform.services.IClientPlatform
-[24nov.2025 23:48:54.174] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.mcp.MCPVersion/CORE]: MCP Version package package net.minecraftforge.versions.mcp, Minecraft, version 1.20.1 from cpw.mods.modlauncher.TransformingClassLoader@753fd7a1
-[24nov.2025 23:48:54.174] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.mcp.MCPVersion/CORE]: Found MC version information 1.20.1
-[24nov.2025 23:48:54.174] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.mcp.MCPVersion/CORE]: Found MCP version information 20230612.114412
-[24nov.2025 23:48:54.174] [modloading-worker-0/INFO] [net.minecraftforge.common.ForgeMod/FORGEMOD]: Forge mod loading, version 47.4.9, for MC 1.20.1 with MCP 20230612.114412
-[24nov.2025 23:48:54.175] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.RegisterParticleProvidersEventHandler to MOD
-[24nov.2025 23:48:54.171] [modloading-worker-0/INFO] [projectatmosphere/]: Auroras detected – enabling seasonal aurora tuning.
-[24nov.2025 23:48:54.176] [modloading-worker-0/INFO] [net.minecraftforge.common.MinecraftForge/FORGE]: MinecraftForge v47.4.9 Initialized
-[24nov.2025 23:48:54.175] [modloading-worker-0/INFO] [projectatmosphere/]: Rainbows detected – enabling precipitation bridge.
-[24nov.2025 23:48:54.176] [modloading-worker-0/DEBUG] [WhiteNoise/]: Loaded technology.roughness.whitenoise.platform.ForgePlatform@20ac33f3 for service interface technology.roughness.whitenoise.platform.services.IPlatform
-[24nov.2025 23:48:54.176] [modloading-worker-0/INFO] [projectatmosphere/]: Tectonic detected – enabling refined ocean geometry.
-[24nov.2025 23:48:54.178] [modloading-worker-0/INFO] [projectatmosphere/]: Continents mod not detected.
-[24nov.2025 23:48:54.182] [modloading-worker-0/DEBUG] [mixin/]: Mixing ParticlesMixins$EngineMixin from chloride.mixin.json into net.minecraft.client.particle.ParticleEngine
-[24nov.2025 23:48:54.209] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.RegisterCommandsEventHandler to FORGE
-[24nov.2025 23:48:54.212] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.PlayerQuitClientEventHandler to FORGE
-[24nov.2025 23:48:54.215] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file sereneseasonsplus-common.toml for sereneseasonsplus tracking
-[24nov.2025 23:48:54.215] [modloading-worker-0/DEBUG] [mixin/]: Mixing BorderlessMixin$WindowMixin from chloride.mixin.json into com.mojang.blaze3d.platform.Window
-[24nov.2025 23:48:54.215] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.PlayerLoggedInEventHandler to FORGE
-[24nov.2025 23:48:54.216] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for sereneseasonsplus
-[24nov.2025 23:48:54.217] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file rainbows-common.toml for rainbows tracking
-[24nov.2025 23:48:54.217] [modloading-worker-0/DEBUG] [mixin/]: Mixing workarounds.context_creation.WindowMixin from embeddium.mixins.json into com.mojang.blaze3d.platform.Window
-[24nov.2025 23:48:54.252] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinRenderTargetAccessor from simpleclouds.mixins.json into com.mojang.blaze3d.pipeline.RenderTarget
-[24nov.2025 23:48:54.254] [modloading-worker-0/DEBUG] [mixin/]: Mixing vivecraft.MixinRenderTarget from simpleclouds.mixins.json into com.mojang.blaze3d.pipeline.RenderTarget
-[24nov.2025 23:48:54.258] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.immediate.matrix_stack.VertexConsumerMixin from embeddium.mixins.json into com.mojang.blaze3d.vertex.VertexConsumer
-[24nov.2025 23:48:54.280] [modloading-worker-0/DEBUG] [mixin/]: Mixing impl.MixinPacketHandler from glitchcore.forge.mixins.json into glitchcore.network.PacketHandler
-[24nov.2025 23:48:54.281] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$init$3(Ljava/lang/String;)Ljava/lang/String; to mdd47aa8$lambda$init$3$0 in glitchcore.forge.mixins.json:impl.MixinPacketHandler
-[24nov.2025 23:48:54.281] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$sendToPlayer$2(Lnet/minecraft/server/level/ServerPlayer;)Lnet/minecraft/server/level/ServerPlayer; to mdd47aa8$lambda$sendToPlayer$2$1 in glitchcore.forge.mixins.json:impl.MixinPacketHandler
-[24nov.2025 23:48:54.281] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$register$1(Lglitchcore/network/CustomPacket;Lglitchcore/network/CustomPacket;Ljava/util/function/Supplier;)V to mdd47aa8$lambda$register$1$2 in glitchcore.forge.mixins.json:impl.MixinPacketHandler
-[24nov.2025 23:48:54.281] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$register$0(Lglitchcore/network/CustomPacket;Lglitchcore/network/CustomPacket;Ljava/util/function/Supplier;)V to mdd47aa8$lambda$register$0$3 in glitchcore.forge.mixins.json:impl.MixinPacketHandler
-[24nov.2025 23:48:54.289] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.GameRendererMixin from auroras.mixins.json into net.minecraft.client.renderer.GameRenderer
-[24nov.2025 23:48:54.290] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinGameRendererAccessor from crackerslib.mixins.json into net.minecraft.client.renderer.GameRenderer
-[24nov.2025 23:48:54.295] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinGameRenderer from simpleclouds.mixins.json into net.minecraft.client.renderer.GameRenderer
-[24nov.2025 23:48:54.296] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$resizeRenderer_resize$0(IILdev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer;)V to mdd47aa8$lambda$simpleclouds$resizeRenderer_resize$0$0 in simpleclouds.mixins.json:MixinGameRenderer
-[24nov.2025 23:48:54.298] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinGameRendererAccessor from simpleclouds.mixins.json into net.minecraft.client.renderer.GameRenderer
-[24nov.2025 23:48:54.300] [modloading-worker-0/DEBUG] [mixin/]: Mixing darkness.GameRendererMixin from chloride.mixin.json into net.minecraft.client.renderer.GameRenderer
-[24nov.2025 23:48:54.301] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.gui.hooks.console.GameRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.GameRenderer
-[24nov.2025 23:48:54.316] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.MixinGuiGraphics from glitchcore.forge.mixins.json into net.minecraft.client.gui.GuiGraphics
-[24nov.2025 23:48:54.318] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.textures.animations.tracking.DrawContextMixin from embeddium.mixins.json into net.minecraft.client.gui.GuiGraphics
-[24nov.2025 23:48:54.325] [modloading-worker-0/DEBUG] [mixin/]: Mixing compat.rainbows.RainbowsRendererParticleMixin from projectatmosphere.mixins.json into rainbows.util.RainbowsRendererParticle
-[24nov.2025 23:48:54.330] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.LevelRenderEventHandler to FORGE
-[24nov.2025 23:48:54.330] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.textures.animations.tracking.SpriteBillboardParticleMixin from embeddium.mixins.json into net.minecraft.client.particle.TextureSheetParticle
-[24nov.2025 23:48:54.334] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.particle.BillboardParticleMixin from embeddium.mixins.json into net.minecraft.client.particle.SingleQuadParticle
-[24nov.2025 23:48:54.335] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for auroras
-[24nov.2025 23:48:54.349] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.options.overlays.InGameHudMixin from embeddium.mixins.json into net.minecraft.client.gui.Gui
-[24nov.2025 23:48:54.356] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.gui.debug.ForgeGuiMixin from embeddium.mixins.json into net.minecraftforge.client.gui.overlay.ForgeGui
-[24nov.2025 23:48:54.357] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$renderLinesVanilla$0(Lme/jellysquid/mods/sodium/mixin/features/render/gui/debug/DebugScreenOverlayAccessor;Lnet/minecraft/client/gui/GuiGraphics;Ljava/util/ArrayList;Ljava/util/ArrayList;)V to mdd47aa8$lambda$renderLinesVanilla$0$0 in embeddium.mixins.json:features.render.gui.debug.ForgeGuiMixin
-[24nov.2025 23:48:54.359] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file forge-client.toml for forge tracking
-[24nov.2025 23:48:54.359] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file forge-server.toml for forge tracking
-[24nov.2025 23:48:54.359] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.ModLoadingContext/]: Attempted to register an empty config for type COMMON on mod forge
-[24nov.2025 23:48:54.378] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinLightTexture from simpleclouds.mixins.json into net.minecraft.client.renderer.LightTexture
-[24nov.2025 23:48:54.379] [modloading-worker-0/DEBUG] [mixin/]: Mixing darkness.LightTextureMixin from chloride.mixin.json into net.minecraft.client.renderer.LightTexture
-[24nov.2025 23:48:54.382] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.Zoom$ModEvents to MOD
-[24nov.2025 23:48:54.383] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for jade
-[24nov.2025 23:48:54.392] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.LevelRendererMixin from auroras.mixins.json into net.minecraft.client.renderer.LevelRenderer
-[24nov.2025 23:48:54.395] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.world.clouds.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
-[24nov.2025 23:48:54.396] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.MixinLevelRenderer from sereneseasons.mixins.json into net.minecraft.client.renderer.LevelRenderer
-[24nov.2025 23:48:54.396] [modloading-worker-0/DEBUG] [mixin/]: Mixing ParticlesMixins$LevelRendererMixin from chloride.mixin.json into net.minecraft.client.renderer.LevelRenderer
-[24nov.2025 23:48:54.396] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.options.weather.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
-[24nov.2025 23:48:54.396] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.gui.outlines.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
-[24nov.2025 23:48:54.400] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.world.sky.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
-[24nov.2025 23:48:54.401] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.render.world.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
-[24nov.2025 23:48:54.401] [modloading-worker-0/WARN] [mixin/]: Static binding violation: PRIVATE @Overwrite method setSectionDirty in embeddium.mixins.json:core.render.world.WorldRendererMixin cannot reduce visibiliy of PUBLIC target method, visibility will be upgraded.
-[24nov.2025 23:48:54.408] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinLevelRenderer from simpleclouds.mixins.json into net.minecraft.client.renderer.LevelRenderer
-[24nov.2025 23:48:54.495] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.render.MatrixStackMixin from embeddium.mixins.json into com.mojang.blaze3d.vertex.PoseStack
-[24nov.2025 23:48:54.498] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.Overlay to FORGE
-[24nov.2025 23:48:54.501] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.textures.animations.tracking.SpriteAtlasTextureMixin from embeddium.mixins.json into net.minecraft.client.renderer.texture.TextureAtlas
-[24nov.2025 23:48:54.516] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.HideNametag to FORGE
-[24nov.2025 23:48:54.534] [modloading-worker-0/DEBUG] [WhiteNoise/CONFIG]: Config file betterdays-client.toml for betterdays added to tracking
-[24nov.2025 23:48:54.535] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinFrustumAccessor from simpleclouds.mixins.json into net.minecraft.client.renderer.culling.Frustum
-[24nov.2025 23:48:54.536] [modloading-worker-0/DEBUG] [WhiteNoise/CONFIG]: Config file betterdays-common.toml for betterdays added to tracking
-[24nov.2025 23:48:54.536] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for betterdays
-[24nov.2025 23:48:54.538] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.render.frustum.FrustumMixin from embeddium.mixins.json into net.minecraft.client.renderer.culling.Frustum
-[24nov.2025 23:48:54.542] [modloading-worker-0/DEBUG] [mixin/]: Mixing NameTagToggleMixin from chloride.mixin.json into net.minecraft.client.renderer.entity.EntityRenderer
-[24nov.2025 23:48:54.542] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.entity.cull.EntityRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.entity.EntityRenderer
-[24nov.2025 23:48:54.549] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.InteractionEventHandler to FORGE
-[24nov.2025 23:48:54.551] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.FastBlocks to FORGE
-[24nov.2025 23:48:54.551] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for rainbows
-[24nov.2025 23:48:54.555] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.ColorsEventHandler to MOD
-[24nov.2025 23:48:54.556] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.FastBlocks$ModEvents to MOD
-[24nov.2025 23:48:54.559] [modloading-worker-0/DEBUG] [mezz.jei.common.platform.Services/]: Loaded mezz.jei.forge.platform.PlatformHelper@36e6b47c for service interface mezz.jei.common.platform.IPlatformHelper
-[24nov.2025 23:48:54.562] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.ChlorideConfig to MOD
-[24nov.2025 23:48:54.563] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.model.colors.ItemColorsMixin from embeddium.mixins.json into net.minecraft.client.color.item.ItemColors
-[24nov.2025 23:48:54.574] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.model.colors.BlockColorsMixin from embeddium.mixins.json into net.minecraft.client.color.block.BlockColors
-[24nov.2025 23:48:54.581] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file simpleclouds-client.toml for simpleclouds tracking
-[24nov.2025 23:48:54.582] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file simpleclouds-common.toml for simpleclouds tracking
-[24nov.2025 23:48:54.582] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file simpleclouds-server.toml for simpleclouds tracking
-[24nov.2025 23:48:54.584] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for forge
-[24nov.2025 23:48:54.584] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.common.ForgeSpawnEggItem$CommonHandler to MOD
-[24nov.2025 23:48:54.588] [modloading-worker-0/DEBUG] [mixin/]: Mixing FontShadowMixin from chloride.mixin.json into net.minecraft.client.gui.Font
-[24nov.2025 23:48:54.588] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for simpleclouds
-[24nov.2025 23:48:54.594] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.gui.screen.LevelLoadingScreenMixin from embeddium.mixins.json into net.minecraft.client.gui.screens.LevelLoadingScreen
-[24nov.2025 23:48:54.594] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$renderChunks$0(Lit/unimi/dsi/fastutil/objects/Object2IntMap$Entry;)V to mdd47aa8$lambda$renderChunks$0$0 in embeddium.mixins.json:features.gui.screen.LevelLoadingScreenMixin
-[24nov.2025 23:48:54.594] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.common.ForgeSpawnEggItem$ColorRegisterHandler to MOD
-[24nov.2025 23:48:54.603] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.client.model.data.ModelDataManager to FORGE
-[24nov.2025 23:48:54.606] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.model.ModelDataMixin from embeddium.mixins.json into net.minecraftforge.client.model.data.ModelData
-[24nov.2025 23:48:54.610] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.client.ForgeHooksClient$ClientEvents to MOD
-[24nov.2025 23:48:54.610] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.Chloride to MOD
-[24nov.2025 23:48:54.610] [modloading-worker-0/ERROR] [Embeddium/]: Failed to update fingerprint
+[12mai2026 16:10:30.668] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: java.nio.DirectByteBuffer.(long, int): unavailable
+[12mai2026 16:10:30.668] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: sun.misc.Unsafe: available
+[12mai2026 16:10:30.669] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: maxDirectMemory: 17112760320 bytes (maybe)
+[12mai2026 16:10:30.669] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.tmpdir: C:\Users\matga\AppData\Local\Temp (java.io.tmpdir)
+[12mai2026 16:10:30.669] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.bitMode: 64 (sun.arch.data.model)
+[12mai2026 16:10:30.669] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: Platform: Windows
+[12mai2026 16:10:30.669] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.maxDirectMemory: -1 bytes
+[12mai2026 16:10:30.670] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.uninitializedArrayAllocationThreshold: -1
+[12mai2026 16:10:30.670] [main/DEBUG] [io.netty.util.internal.CleanerJava9/]: java.nio.ByteBuffer.cleaner(): available
+[12mai2026 16:10:30.670] [main/DEBUG] [io.netty.util.internal.PlatformDependent/]: -Dio.netty.noPreferDirect: false
+[12mai2026 16:10:30.701] [main/DEBUG] [net.minecraftforge.network.NetworkHooks/]: Loading Network data for FML net version: FML3
+[12mai2026 16:10:30.711] [main/DEBUG] [net.minecraftforge.fml.ModWorkManager/LOADING]: Using 24 threads for parallel mod-loading
+[12mai2026 16:10:30.714] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.715] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for betterdays.BetterDaysForge
+[12mai2026 16:10:30.717] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.717] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for nonamecrackers2.crackerslib.CrackersLib
+[12mai2026 16:10:30.717] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.717] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for com.llamalad7.mixinextras.platform.forge.MixinExtrasMod
+[12mai2026 16:10:30.718] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.718] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for glitchcore.forge.GlitchCoreForge
+[12mai2026 16:10:30.718] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.718] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for sereneseasons.forge.core.SereneSeasonsForge
+[12mai2026 16:10:30.718] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.718] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for snownee.jade.util.CommonProxy
+[12mai2026 16:10:30.719] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.719] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for dev.nonamecrackers2.simpleclouds.SimpleCloudsMod
+[12mai2026 16:10:30.720] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.721] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for dev.architectury.forge.ArchitecturyForge
+[12mai2026 16:10:30.721] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.721] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for technology.roughness.whitenoise.WhiteNoiseForge
+[12mai2026 16:10:30.721] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.721] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for com.seibel.distanthorizons.forge.ForgeMain
+[12mai2026 16:10:30.722] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.722] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for mezz.jei.forge.JustEnoughItems
+[12mai2026 16:10:30.722] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.722] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for dev.worldgen.lithostitched.LithostitchedForge
+[12mai2026 16:10:30.722] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.722] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.shedaniel.clothconfig.ClothConfigForge
+[12mai2026 16:10:30.723] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.723] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.lucko.spark.forge.ForgeSparkMod
+[12mai2026 16:10:30.724] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.724] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for net.Gabou.gaboulibs.forge.GaboulibsForge
+[12mai2026 16:10:30.724] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.724] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for net.minecraftforge.common.ForgeMod
+[12mai2026 16:10:30.725] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.725] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.jellysquid.mods.sodium.client.SodiumClientMod
+[12mai2026 16:10:30.727] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.727] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.jellysquid.mods.sodium.client.RubidiumStub
+[12mai2026 16:10:30.727] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.727] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for me.srrapero720.chloride.Chloride
+[12mai2026 16:10:30.727] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.727] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for net.Gabou.projectatmosphere.ProjectAtmosphere
+[12mai2026 16:10:30.729] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.729] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for com.Gabou.sereneseasonsplus.SereneSeasonsPlusForge
+[12mai2026 16:10:30.729] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider/LOADING]: Loading FMLModContainer from classloader cpw.mods.modlauncher.TransformingClassLoader@7c663eaf - got cpw.mods.cl.ModuleClassLoader@a64e035
+[12mai2026 16:10:30.730] [main/DEBUG] [net.minecraftforge.fml.javafmlmod.FMLModContainer/LOADING]: Creating FMLModContainer instance for dev.worldgen.tectonic.TectonicLexforge
+[12mai2026 16:10:30.741] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for glitchcore
+[12mai2026 16:10:30.741] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for mixinextras
+[12mai2026 16:10:30.747] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for rubidium
+[12mai2026 16:10:30.747] [modloading-worker-0/INFO] [chloride/Main]: Chloride is here, lets make your experience taste-able
+[12mai2026 16:10:30.747] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for chloride
+[12mai2026 16:10:30.747] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for distanthorizons
+[12mai2026 16:10:30.747] [modloading-worker-0/INFO] [projectatmosphere/]: Project Atmosphere is loading!
+[12mai2026 16:10:30.751] [modloading-worker-0/DEBUG] [Better Days/]: Loaded betterdays.platform.ForgeClientPlatform@6a7ea8a4 for service interface betterdays.platform.services.IClientPlatform
+[12mai2026 16:10:30.754] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for cloth_config
+[12mai2026 16:10:30.755] [modloading-worker-0/DEBUG] [mixin/]: Mixing MinecraftServerMixin from sereneseasonsplus.mixins.json into net.minecraft.server.MinecraftServer
+[12mai2026 16:10:30.755] [modloading-worker-0/DEBUG] [Better Days/]: Loaded betterdays.platform.ForgePlatform@359f893c for service interface betterdays.platform.services.IPlatform
+[12mai2026 16:10:30.758] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinLevelEvent from architectury.mixins.json into net.minecraftforge.event.level.LevelEvent
+[12mai2026 16:10:30.771] [modloading-worker-0/DEBUG] [Better Days/]: Loaded betterdays.platform.ForgeRegistryProvider@110907bb for service interface betterdays.platform.services.IRegistryFactory
+[12mai2026 16:10:30.773] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.sodium.SodiumFeatures to FORGE
+[12mai2026 16:10:30.773] [modloading-worker-0/DEBUG] [mixin/]: Mixing impl.MixinEnvironment from glitchcore.forge.mixins.json into glitchcore.util.Environment
+[12mai2026 16:10:30.773] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.VillagerTradesEventHandler to FORGE
+[12mai2026 16:10:30.804] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.TooltipEventHandler to FORGE
+[12mai2026 16:10:30.814] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for spark
+[12mai2026 16:10:30.822] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.ToolModificationEventHandler to FORGE
+[12mai2026 16:10:30.824] [modloading-worker-0/DEBUG] [mixin/]: Mixing vivecraft.MixinMinecraftVRMixin from simpleclouds.mixins.json into net.minecraft.client.Minecraft
+[12mai2026 16:10:30.824] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$onVRStateSwitched_vivecrafft$switchVRSate$0(Ldev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer;)V to mdb27d7e$lambda$simpleclouds$onVRStateSwitched_vivecrafft$switchVRSate$0$0 in simpleclouds.mixins.json:vivecraft.MixinMinecraftVRMixin
+[12mai2026 16:10:30.824] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for whitenoise
+[12mai2026 16:10:30.824] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.MinecraftCrashHandlerMixin from projectatmosphere.mixins.json into net.minecraft.client.Minecraft
+[12mai2026 16:10:30.827] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.MixinMinecraft from glitchcore.mixins.json into net.minecraft.client.Minecraft
+[12mai2026 16:10:30.833] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinMinecraft from simpleclouds.mixins.json into net.minecraft.client.Minecraft
+[12mai2026 16:10:30.833] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$onClientLevelChange_setLevel$1(Lnet/minecraft/client/multiplayer/ClientLevel;Ldev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer;)V to mdb27d7e$lambda$simpleclouds$onClientLevelChange_setLevel$1$1 in simpleclouds.mixins.json:MixinMinecraft
+[12mai2026 16:10:30.833] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$appendCrashReportDetails_fillReport$0(Lnet/minecraft/CrashReport;Ldev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer;)V to mdb27d7e$lambda$simpleclouds$appendCrashReportDetails_fillReport$0$2 in simpleclouds.mixins.json:MixinMinecraft
+[12mai2026 16:10:30.839] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinMinecraft from architectury.mixins.json into net.minecraft.client.Minecraft
+[12mai2026 16:10:30.840] [modloading-worker-0/DEBUG] [mixin/]: Mixing MinecraftMixin from chloride.mixin.json into net.minecraft.client.Minecraft
+[12mai2026 16:10:30.840] [modloading-worker-0/DEBUG] [mixin/]: Mixing OverlayMixin from chloride.mixin.json into net.minecraft.client.Minecraft
+[12mai2026 16:10:30.842] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.render.MinecraftAccessor from embeddium.mixins.json into net.minecraft.client.Minecraft
+[12mai2026 16:10:30.843] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.MinecraftClientMixin from embeddium.mixins.json into net.minecraft.client.Minecraft
+[12mai2026 16:10:30.843] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$postInit$0(Lnet/minecraft/client/gui/screens/Screen;)Lnet/minecraft/client/gui/screens/Screen; to mdb27d7e$lambda$postInit$0$3 in embeddium.mixins.json:core.MinecraftClientMixin
+[12mai2026 16:10:30.926] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.Zoom to FORGE
+[12mai2026 16:10:30.926] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.TickEventHandler to FORGE
+[12mai2026 16:10:30.930] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.TagsUpdatedEventHandler to FORGE
+[12mai2026 16:10:30.932] [modloading-worker-0/WARN] [CompatUtils/]: Detected both Project Atmosphere and Serene Seasons Plus; running with both may be unstable. Elected host: PROJECT_ATMOSPHERE
+[12mai2026 16:10:30.933] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for gaboulibs
+[12mai2026 16:10:30.938] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.RegistryEventHandler to MOD
+[12mai2026 16:10:30.939] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file projectatmosphere-common.toml for projectatmosphere tracking
+[12mai2026 16:10:30.940] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file crackerslib-client.toml for crackerslib tracking
+[12mai2026 16:10:30.940] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file jei-server.toml for jei tracking
+[12mai2026 16:10:30.941] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.forge.ForgeVersion/CORE]: Forge Version package package net.minecraftforge.versions.forge, Forge, version 47.4 from cpw.mods.modlauncher.TransformingClassLoader@7c663eaf
+[12mai2026 16:10:30.942] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.RegisterParticleProvidersEventHandler to MOD
+[12mai2026 16:10:30.942] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.forge.ForgeVersion/CORE]: Found Forge version 47.4.13
+[12mai2026 16:10:30.942] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.forge.ForgeVersion/CORE]: Found Forge spec 47.4
+[12mai2026 16:10:30.942] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.forge.ForgeVersion/CORE]: Found Forge group net.minecraftforge
+[12mai2026 16:10:30.944] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.mcp.MCPVersion/CORE]: MCP Version package package net.minecraftforge.versions.mcp, Minecraft, version 1.20.1 from cpw.mods.modlauncher.TransformingClassLoader@7c663eaf
+[12mai2026 16:10:30.944] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.mcp.MCPVersion/CORE]: Found MC version information 1.20.1
+[12mai2026 16:10:30.944] [modloading-worker-0/DEBUG] [net.minecraftforge.versions.mcp.MCPVersion/CORE]: Found MCP version information 20230612.114412
+[12mai2026 16:10:30.944] [modloading-worker-0/INFO] [net.minecraftforge.common.ForgeMod/FORGEMOD]: Forge mod loading, version 47.4.13, for MC 1.20.1 with MCP 20230612.114412
+[12mai2026 16:10:30.945] [modloading-worker-0/DEBUG] [WhiteNoise/]: Loaded technology.roughness.whitenoise.platform.ForgeConfigHelper@4cebbfbc for service interface technology.roughness.whitenoise.platform.services.IConfigHelper
+[12mai2026 16:10:30.945] [modloading-worker-0/INFO] [net.minecraftforge.common.MinecraftForge/FORGE]: MinecraftForge v47.4.13 Initialized
+[12mai2026 16:10:30.946] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for crackerslib
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: No temperature mod loaded, skipping compatibility setup.
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: Sand Storms mod not found.
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: Auroras mod not detected.
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: Rainbows mod not detected.
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: Tectonic detected - enabling refined ocean geometry.
+[12mai2026 16:10:30.947] [modloading-worker-0/INFO] [projectatmosphere/]: Continents mod not detected.
+[12mai2026 16:10:30.947] [modloading-worker-0/INFO] [projectatmosphere/]: Dynamic Trees not detected.
+[12mai2026 16:10:30.947] [modloading-worker-0/DEBUG] [WhiteNoise/]: Loaded technology.roughness.whitenoise.platform.ForgeClientPlatform@230d6764 for service interface technology.roughness.whitenoise.platform.services.IClientPlatform
+[12mai2026 16:10:30.947] [modloading-worker-0/DEBUG] [mixin/]: Mixing SimpleCloudsCloudManagerMixin from projectatmosphere.mixins.json into dev.nonamecrackers2.simpleclouds.common.world.CloudManager
+[12mai2026 16:10:30.953] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.particle.WindBentParticleEngineMixin from projectatmosphere.mixins.json into net.minecraft.client.particle.ParticleEngine
+[12mai2026 16:10:30.953] [modloading-worker-0/DEBUG] [WhiteNoise/]: Loaded technology.roughness.whitenoise.platform.ForgePlatform@3b97c459 for service interface technology.roughness.whitenoise.platform.services.IPlatform
+[12mai2026 16:10:30.954] [modloading-worker-0/DEBUG] [mixin/]: Mixing ParticlesMixins$EngineMixin from chloride.mixin.json into net.minecraft.client.particle.ParticleEngine
+[12mai2026 16:10:30.972] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinRenderTargetAccessor from simpleclouds.mixins.json into com.mojang.blaze3d.pipeline.RenderTarget
+[12mai2026 16:10:30.974] [modloading-worker-0/DEBUG] [mixin/]: Mixing vivecraft.MixinRenderTarget from simpleclouds.mixins.json into com.mojang.blaze3d.pipeline.RenderTarget
+[12mai2026 16:10:30.978] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.RegisterCommandsEventHandler to FORGE
+[12mai2026 16:10:30.981] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.PlayerQuitClientEventHandler to FORGE
+[12mai2026 16:10:30.983] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.options.overlays.InGameHudMixin from embeddium.mixins.json into net.minecraft.client.gui.Gui
+[12mai2026 16:10:30.988] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.PlayerLoggedInEventHandler to FORGE
+[12mai2026 16:10:30.991] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.LevelRenderEventHandler to FORGE
+[12mai2026 16:10:30.995] [modloading-worker-0/DEBUG] [mixin/]: Mixing BorderlessMixin$WindowMixin from chloride.mixin.json into com.mojang.blaze3d.platform.Window
+[12mai2026 16:10:30.997] [modloading-worker-0/DEBUG] [mixin/]: Mixing workarounds.context_creation.WindowMixin from embeddium.mixins.json into com.mojang.blaze3d.platform.Window
+[12mai2026 16:10:31.036] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.gui.debug.ForgeGuiMixin from embeddium.mixins.json into net.minecraftforge.client.gui.overlay.ForgeGui
+[12mai2026 16:10:31.037] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$renderLinesVanilla$0(Lme/jellysquid/mods/sodium/mixin/features/render/gui/debug/DebugScreenOverlayAccessor;Lnet/minecraft/client/gui/GuiGraphics;Ljava/util/ArrayList;Ljava/util/ArrayList;)V to mdb27d7e$lambda$renderLinesVanilla$0$0 in embeddium.mixins.json:features.render.gui.debug.ForgeGuiMixin
+[12mai2026 16:10:31.081] [modloading-worker-0/DEBUG] [mixin/]: Mixing impl.MixinPacketHandler from glitchcore.forge.mixins.json into glitchcore.network.PacketHandler
+[12mai2026 16:10:31.081] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$init$3(Ljava/lang/String;)Ljava/lang/String; to mdb27d7e$lambda$init$3$0 in glitchcore.forge.mixins.json:impl.MixinPacketHandler
+[12mai2026 16:10:31.081] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$sendToPlayer$2(Lnet/minecraft/server/level/ServerPlayer;)Lnet/minecraft/server/level/ServerPlayer; to mdb27d7e$lambda$sendToPlayer$2$1 in glitchcore.forge.mixins.json:impl.MixinPacketHandler
+[12mai2026 16:10:31.081] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$register$1(Lglitchcore/network/CustomPacket;Lglitchcore/network/CustomPacket;Ljava/util/function/Supplier;)V to mdb27d7e$lambda$register$1$2 in glitchcore.forge.mixins.json:impl.MixinPacketHandler
+[12mai2026 16:10:31.081] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$register$0(Lglitchcore/network/CustomPacket;Lglitchcore/network/CustomPacket;Ljava/util/function/Supplier;)V to mdb27d7e$lambda$register$0$3 in glitchcore.forge.mixins.json:impl.MixinPacketHandler
+[12mai2026 16:10:31.083] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file sereneseasonsplus-common.toml for sereneseasonsplus tracking
+[12mai2026 16:10:31.084] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for sereneseasonsplus
+[12mai2026 16:10:31.090] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.MixinGuiGraphics from glitchcore.forge.mixins.json into net.minecraft.client.gui.GuiGraphics
+[12mai2026 16:10:31.092] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.textures.animations.tracking.DrawContextMixin from embeddium.mixins.json into net.minecraft.client.gui.GuiGraphics
+[12mai2026 16:10:31.109] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.world.clouds.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
+[12mai2026 16:10:31.111] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.MixinLevelRenderer from sereneseasons.mixins.json into net.minecraft.client.renderer.LevelRenderer
+[12mai2026 16:10:31.112] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.MixinLevelRenderer from DistantHorizons.forge.mixins.json into net.minecraft.client.renderer.LevelRenderer
+[12mai2026 16:10:31.117] [modloading-worker-0/DEBUG] [mixin/]: Mixing ParticlesMixins$LevelRendererMixin from chloride.mixin.json into net.minecraft.client.renderer.LevelRenderer
+[12mai2026 16:10:31.117] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.options.weather.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
+[12mai2026 16:10:31.117] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.gui.outlines.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
+[12mai2026 16:10:31.143] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.world.sky.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
+[12mai2026 16:10:31.144] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.render.world.WorldRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.LevelRenderer
+[12mai2026 16:10:31.145] [modloading-worker-0/WARN] [mixin/]: Static binding violation: PRIVATE @Overwrite method setSectionDirty in embeddium.mixins.json:core.render.world.WorldRendererMixin cannot reduce visibiliy of PUBLIC target method, visibility will be upgraded.
+[12mai2026 16:10:31.150] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinLevelRenderer from simpleclouds.mixins.json into net.minecraft.client.renderer.LevelRenderer
+[12mai2026 16:10:31.244] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.immediate.matrix_stack.VertexConsumerMixin from embeddium.mixins.json into com.mojang.blaze3d.vertex.VertexConsumer
+[12mai2026 16:10:31.248] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinLightTexture from simpleclouds.mixins.json into net.minecraft.client.renderer.LightTexture
+[12mai2026 16:10:31.248] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.MixinLightTexture from DistantHorizons.forge.mixins.json into net.minecraft.client.renderer.LightTexture
+[12mai2026 16:10:31.250] [modloading-worker-0/DEBUG] [mixin/]: Mixing darkness.LightTextureMixin from chloride.mixin.json into net.minecraft.client.renderer.LightTexture
+[12mai2026 16:10:31.254] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for jade
+[12mai2026 16:10:31.269] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinGameRendererAccessor from crackerslib.mixins.json into net.minecraft.client.renderer.GameRenderer
+[12mai2026 16:10:31.281] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinGameRenderer from simpleclouds.mixins.json into net.minecraft.client.renderer.GameRenderer
+[12mai2026 16:10:31.281] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$simpleclouds$resizeRenderer_resize$0(IILdev/nonamecrackers2/simpleclouds/client/renderer/SimpleCloudsRenderer;)V to mdb27d7e$lambda$simpleclouds$resizeRenderer_resize$0$0 in simpleclouds.mixins.json:MixinGameRenderer
+[12mai2026 16:10:31.283] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinGameRendererAccessor from simpleclouds.mixins.json into net.minecraft.client.renderer.GameRenderer
+[12mai2026 16:10:31.286] [modloading-worker-0/DEBUG] [mixin/]: Mixing darkness.GameRendererMixin from chloride.mixin.json into net.minecraft.client.renderer.GameRenderer
+[12mai2026 16:10:31.287] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.gui.hooks.console.GameRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.GameRenderer
+[12mai2026 16:10:31.307] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.render.MatrixStackMixin from embeddium.mixins.json into com.mojang.blaze3d.vertex.PoseStack
+[12mai2026 16:10:31.319] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file forge-client.toml for forge tracking
+[12mai2026 16:10:31.319] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file forge-server.toml for forge tracking
+[12mai2026 16:10:31.319] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.ModLoadingContext/]: Attempted to register an empty config for type COMMON on mod forge
+[12mai2026 16:10:31.338] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.LoadingScreenMixin from projectatmosphere.mixins.json into net.minecraft.client.gui.screens.LevelLoadingScreen
+[12mai2026 16:10:31.339] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.gui.screen.LevelLoadingScreenMixin from embeddium.mixins.json into net.minecraft.client.gui.screens.LevelLoadingScreen
+[12mai2026 16:10:31.339] [modloading-worker-0/DEBUG] [mixin/]: Renaming synthetic method lambda$renderChunks$0(Lit/unimi/dsi/fastutil/objects/Object2IntMap$Entry;)V to mdb27d7e$lambda$renderChunks$0$0 in embeddium.mixins.json:features.gui.screen.LevelLoadingScreenMixin
+[12mai2026 16:10:31.356] [modloading-worker-0/DEBUG] [mixin/]: Mixing MixinFrustumAccessor from simpleclouds.mixins.json into net.minecraft.client.renderer.culling.Frustum
+[12mai2026 16:10:31.356] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.Zoom$ModEvents to MOD
+[12mai2026 16:10:31.357] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.render.frustum.FrustumMixin from embeddium.mixins.json into net.minecraft.client.renderer.culling.Frustum
+[12mai2026 16:10:31.362] [modloading-worker-0/DEBUG] [mixin/]: Mixing client.LoadingOverlayMixin from projectatmosphere.mixins.json into net.minecraft.client.gui.screens.LoadingOverlay
+[12mai2026 16:10:31.365] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.Overlay to FORGE
+[12mai2026 16:10:31.366] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.InteractionEventHandler to FORGE
+[12mai2026 16:10:31.368] [modloading-worker-0/DEBUG] [mezz.jei.common.platform.Services/]: Loaded mezz.jei.forge.platform.PlatformHelper@66f7f037 for service interface mezz.jei.common.platform.IPlatformHelper
+[12mai2026 16:10:31.371] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing glitchcore.forge.handlers.ColorsEventHandler to MOD
+[12mai2026 16:10:31.375] [modloading-worker-0/ERROR] [Embeddium/]: Failed to update fingerprint
java.lang.NullPointerException: Cannot invoke "net.minecraft.client.Minecraft.getUser()" because the return value of "net.minecraft.client.Minecraft.getInstance()" is null
- at me.jellysquid.mods.sodium.client.data.fingerprint.FingerprintMeasure.create(FingerprintMeasure.java:19) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23214!/:?]
- at me.jellysquid.mods.sodium.client.SodiumClientMod.updateFingerprint(SodiumClientMod.java:108) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23214!/:?]
- at me.jellysquid.mods.sodium.client.SodiumClientMod.(SodiumClientMod.java:48) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23214!/:?]
+ at me.jellysquid.mods.sodium.client.data.fingerprint.FingerprintMeasure.create(FingerprintMeasure.java:19) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23213!/:?]
+ at me.jellysquid.mods.sodium.client.SodiumClientMod.updateFingerprint(SodiumClientMod.java:108) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23213!/:?]
+ at me.jellysquid.mods.sodium.client.SodiumClientMod.(SodiumClientMod.java:48) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23213!/:?]
at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[?:?]
at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?]
at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) ~[?:?]
at java.lang.reflect.Constructor.newInstance(Constructor.java:481) ~[?:?]
- at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:77) ~[javafmllanguage-1.20.1-47.4.9.jar%23192!/:?]
- at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$5(ModContainer.java:126) ~[fmlcore-1.20.1-47.4.9.jar%23195!/:?]
+ at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:77) ~[javafmllanguage-1.20.1-47.4.13.jar%23192!/:?]
+ at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$5(ModContainer.java:126) ~[fmlcore-1.20.1-47.4.13.jar%23195!/:?]
at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[?:?]
at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) ~[?:?]
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?]
@@ -1103,239 +1067,271 @@ java.lang.NullPointerException: Cannot invoke "net.minecraft.client.Minecraft.ge
at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?]
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?]
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?]
-[24nov.2025 23:48:54.611] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for embeddium
-[24nov.2025 23:48:54.611] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing org.embeddedt.embeddium.render.frapi.SpriteFinderCache to MOD
-[24nov.2025 23:48:54.613] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.shader.uniform.ShaderProgramMixin from embeddium.mixins.json into net.minecraft.client.renderer.ShaderInstance
-[24nov.2025 23:48:54.614] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for projectatmosphere
-[24nov.2025 23:48:54.614] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.ClientRenderHook to FORGE
-[24nov.2025 23:48:54.622] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.textures.animations.tracking.SpriteMixin from embeddium.mixins.json into net.minecraft.client.renderer.texture.TextureAtlasSprite
-[24nov.2025 23:48:54.628] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.HUDOverlayRenderer to FORGE
-[24nov.2025 23:48:54.631] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.render.HudRenderTest to FORGE
-[24nov.2025 23:48:54.632] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing org.embeddedt.embeddium.impl.ConfigScreenHandlerHook to MOD
-[24nov.2025 23:48:54.632] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for sereneseasons
-[24nov.2025 23:48:54.632] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing sereneseasons.forge.datagen.DataGenerationHandler to MOD
-[24nov.2025 23:48:54.633] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.TornadoShaders to MOD
-[24nov.2025 23:48:54.635] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing org.embeddedt.embeddium.compat.immersive.ImmersiveCompat to MOD
-[24nov.2025 23:48:54.635] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.command.CloudDumpCommand to FORGE
-[24nov.2025 23:48:54.636] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing org.embeddedt.embeddium.compat.ccl.CCLCompatBootstrap to MOD
-[24nov.2025 23:48:54.638] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.event.BiomeChangeManager to FORGE
-[24nov.2025 23:48:54.638] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.event.EventHandler to FORGE
-[24nov.2025 23:48:54.638] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.event.SimpleCloudsEventListener to FORGE
-[24nov.2025 23:48:54.643] [modloading-worker-0/DEBUG] [mixin/]: Mixing CloudRegionTickEventMixin from projectatmosphere.mixins.json into dev.nonamecrackers2.simpleclouds.api.common.event.CloudRegionTickEvent
-[24nov.2025 23:48:54.651] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.client.ClientForgeMod to MOD
-[24nov.2025 23:48:54.653] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.event.TemperatureTickHandler to FORGE
-[24nov.2025 23:48:54.653] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.modules.hurricane.HurricaneCommand to FORGE
-[24nov.2025 23:48:54.655] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.modules.tornado.TornadoCommand to FORGE
-[24nov.2025 23:48:54.659] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.modules.tornado.TornadoDebug to FORGE
-[24nov.2025 23:48:54.662] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.ProjectAtmosphere to FORGE
-[24nov.2025 23:48:54.666] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.registry.ModClient to MOD
-[24nov.2025 23:48:54.668] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.util.CloudSpawnScheduler to FORGE
-[24nov.2025 23:48:54.670] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.util.ParticleAtlasDebugger to FORGE
-[24nov.2025 23:48:54.680] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for tectonic
-[24nov.2025 23:48:54.680] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.worldgen.tectonic.TectonicLexforge$ModBusEvents to MOD
-[24nov.2025 23:48:54.692] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Loading config file: G:\Project-Atmosphere\run-data\config\jei\jei-debug.ini
-[24nov.2025 23:48:54.695] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Saving config file: G:\Project-Atmosphere\run-data\config\jei\jei-debug.ini
-[24nov.2025 23:48:54.705] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Loading config file: G:\Project-Atmosphere\run-data\config\jei\jei-mod-id-format.ini
-[24nov.2025 23:48:54.706] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Saving config file: G:\Project-Atmosphere\run-data\config\jei\jei-mod-id-format.ini
-[24nov.2025 23:48:54.711] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Loading config file: G:\Project-Atmosphere\run-data\config\jei\jei-colors.ini
-[24nov.2025 23:48:54.715] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Saving config file: G:\Project-Atmosphere\run-data\config\jei\jei-colors.ini
-[24nov.2025 23:48:54.718] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for architectury
-[24nov.2025 23:48:54.718] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.level.entity.trade.forge.TradeRegistryImpl to FORGE
-[24nov.2025 23:48:54.719] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for lithostitched
-[24nov.2025 23:48:54.721] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.forge.ReloadListenerRegistryImpl to FORGE
-[24nov.2025 23:48:54.722] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.forge.CreativeTabRegistryImpl to MOD
-[24nov.2025 23:48:54.728] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Loading config file: G:\Project-Atmosphere\run-data\config\jei\jei-client.ini
-[24nov.2025 23:48:54.730] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Saving config file: G:\Project-Atmosphere\run-data\config\jei\jei-client.ini
-[24nov.2025 23:48:54.733] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.client.particle.forge.ParticleProviderRegistryImpl to MOD
-[24nov.2025 23:48:54.736] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.client.keymappings.forge.KeyMappingRegistryImpl to MOD
-[24nov.2025 23:48:54.736] [modloading-worker-0/INFO] [mezz.jei.library.load.PluginCaller/]: Sending ConfigManager...
-[24nov.2025 23:48:54.736] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.networking.forge.NetworkManagerImpl to FORGE
-[24nov.2025 23:48:54.742] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:minecraft...
-[24nov.2025 23:48:54.742] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jade:main...
-[24nov.2025 23:48:54.742] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:debug...
-[24nov.2025 23:48:54.742] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:gui...
-[24nov.2025 23:48:54.742] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:forge_gui...
-[24nov.2025 23:48:54.742] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:internal...
-[24nov.2025 23:48:54.744] [modloading-worker-0/INFO] [mezz.jei.library.load.PluginCaller/]: Sending ConfigManager took 4.637 ms
-[24nov.2025 23:48:54.746] [modloading-worker-0/INFO] [dev.architectury.networking.forge.NetworkManagerImpl/]: Registering S2C receiver with id architectury:sync_ids
-[24nov.2025 23:48:54.748] [modloading-worker-0/INFO] [dev.architectury.networking.forge.NetworkManagerImpl/]: Registering C2S receiver with id architectury:sync_ids
-[24nov.2025 23:48:54.750] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for jei
-[24nov.2025 23:48:54.755] [main/DEBUG] [WhiteNoise/CONFIG]: Loading global configs
-[24nov.2025 23:48:54.759] [main/DEBUG] [WhiteNoise/CONFIG]: Built TOML config for G:\Project-Atmosphere\run-data\config\betterdays-client.toml
-[24nov.2025 23:48:54.760] [main/DEBUG] [WhiteNoise/CONFIG]: Loaded TOML config file G:\Project-Atmosphere\run-data\config\betterdays-client.toml
-[24nov.2025 23:48:54.768] [main/DEBUG] [WhiteNoise/CONFIG]: Built TOML config for G:\Project-Atmosphere\run-data\config\betterdays-common.toml
-[24nov.2025 23:48:54.768] [main/DEBUG] [WhiteNoise/CONFIG]: Loaded TOML config file G:\Project-Atmosphere\run-data\config\betterdays-common.toml
-[24nov.2025 23:48:54.777] [main/DEBUG] [mixin/]: Mixing common.RegistryDataLoaderMixin from lithostitched.mixins.json into net.minecraft.resources.RegistryDataLoader
-[24nov.2025 23:48:54.798] [main/DEBUG] [net.minecraftforge.registries.ObjectHolderRegistry/REGISTRIES]: Processing ObjectHolder annotations
-[24nov.2025 23:48:54.808] [main/DEBUG] [net.minecraftforge.registries.ObjectHolderRegistry/REGISTRIES]: Found 3933 ObjectHolder annotations
-[24nov.2025 23:48:54.810] [main/DEBUG] [net.minecraftforge.common.capabilities.CapabilityManager/CAPABILITIES]: Attempting to automatically register: Lnet/minecraftforge/energy/IEnergyStorage;
-[24nov.2025 23:48:54.811] [main/DEBUG] [net.minecraftforge.common.capabilities.CapabilityManager/CAPABILITIES]: Attempting to automatically register: Lnet/minecraftforge/fluids/capability/IFluidHandler;
-[24nov.2025 23:48:54.811] [main/DEBUG] [net.minecraftforge.common.capabilities.CapabilityManager/CAPABILITIES]: Attempting to automatically register: Lnet/minecraftforge/fluids/capability/IFluidHandlerItem;
-[24nov.2025 23:48:54.811] [main/DEBUG] [net.minecraftforge.common.capabilities.CapabilityManager/CAPABILITIES]: Attempting to automatically register: Lnet/minecraftforge/items/IItemHandler;
-[24nov.2025 23:48:54.811] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Unfreezing vanilla registries
-[24nov.2025 23:48:54.909] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:sound_event
-[24nov.2025 23:48:54.911] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:sound_event
-[24nov.2025 23:48:54.911] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:fluid
-[24nov.2025 23:48:54.912] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:fluid
-[24nov.2025 23:48:54.920] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:block
-[24nov.2025 23:48:54.921] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:block
-[24nov.2025 23:48:54.921] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:attribute
-[24nov.2025 23:48:54.921] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:attribute
-[24nov.2025 23:48:54.922] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:mob_effect
-[24nov.2025 23:48:54.922] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:mob_effect
-[24nov.2025 23:48:54.924] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:particle_type
-[24nov.2025 23:48:54.925] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:particle_type
-[24nov.2025 23:48:54.951] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:item
-[24nov.2025 23:48:54.952] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:item
-[24nov.2025 23:48:54.952] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:entity_type
-[24nov.2025 23:48:54.952] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:entity_type
-[24nov.2025 23:48:54.952] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:sensor_type
-[24nov.2025 23:48:54.952] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:sensor_type
-[24nov.2025 23:48:54.953] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:memory_module_type
-[24nov.2025 23:48:54.953] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:memory_module_type
-[24nov.2025 23:48:54.953] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:potion
-[24nov.2025 23:48:54.953] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:potion
-[24nov.2025 23:48:54.954] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:game_event
-[24nov.2025 23:48:54.954] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:game_event
-[24nov.2025 23:48:54.954] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:enchantment
-[24nov.2025 23:48:54.954] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:enchantment
-[24nov.2025 23:48:54.955] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:block_entity_type
-[24nov.2025 23:48:54.956] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:block_entity_type
-[24nov.2025 23:48:54.956] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:painting_variant
-[24nov.2025 23:48:54.956] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:painting_variant
-[24nov.2025 23:48:54.956] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:stat_type
-[24nov.2025 23:48:54.956] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:stat_type
-[24nov.2025 23:48:54.957] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:custom_stat
-[24nov.2025 23:48:54.957] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:custom_stat
-[24nov.2025 23:48:54.957] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:chunk_status
-[24nov.2025 23:48:54.957] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:chunk_status
-[24nov.2025 23:48:54.957] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:rule_test
-[24nov.2025 23:48:54.957] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:rule_test
-[24nov.2025 23:48:54.958] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:rule_block_entity_modifier
-[24nov.2025 23:48:54.958] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:rule_block_entity_modifier
-[24nov.2025 23:48:54.958] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:pos_rule_test
-[24nov.2025 23:48:54.958] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:pos_rule_test
-[24nov.2025 23:48:54.958] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:menu
-[24nov.2025 23:48:54.959] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:menu
-[24nov.2025 23:48:54.959] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:recipe_type
-[24nov.2025 23:48:54.959] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:recipe_type
-[24nov.2025 23:48:54.981] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:recipe_serializer
-[24nov.2025 23:48:54.982] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:recipe_serializer
-[24nov.2025 23:48:54.982] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:position_source_type
-[24nov.2025 23:48:54.982] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:position_source_type
-[24nov.2025 23:48:54.987] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:command_argument_type
-[24nov.2025 23:48:54.987] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:command_argument_type
-[24nov.2025 23:48:54.987] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:villager_type
-[24nov.2025 23:48:54.987] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:villager_type
-[24nov.2025 23:48:54.988] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:villager_profession
-[24nov.2025 23:48:54.988] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:villager_profession
-[24nov.2025 23:48:54.988] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:point_of_interest_type
-[24nov.2025 23:48:54.988] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:point_of_interest_type
-[24nov.2025 23:48:54.988] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:schedule
-[24nov.2025 23:48:54.989] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:schedule
-[24nov.2025 23:48:54.989] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:activity
-[24nov.2025 23:48:54.989] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:activity
-[24nov.2025 23:48:54.989] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_pool_entry_type
-[24nov.2025 23:48:54.989] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_pool_entry_type
-[24nov.2025 23:48:54.989] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_function_type
-[24nov.2025 23:48:54.989] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_function_type
-[24nov.2025 23:48:54.993] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_condition_type
-[24nov.2025 23:48:54.993] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_condition_type
-[24nov.2025 23:48:54.993] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_number_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_number_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_nbt_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_nbt_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_score_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_score_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:float_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:float_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:int_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:int_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:height_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:height_provider_type
-[24nov.2025 23:48:54.994] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:block_predicate_type
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:block_predicate_type
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/carver
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/carver
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/feature
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/feature
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_processor
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_processor
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_placement
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_placement
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_piece
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_piece
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_type
-[24nov.2025 23:48:54.995] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/placement_modifier_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/placement_modifier_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/block_state_provider_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/block_state_provider_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/foliage_placer_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/foliage_placer_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/trunk_placer_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/trunk_placer_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/root_placer_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/root_placer_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/tree_decorator_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/tree_decorator_type
-[24nov.2025 23:48:54.996] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/feature_size_type
-[24nov.2025 23:48:54.997] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/feature_size_type
-[24nov.2025 23:48:54.997] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/biome_source
-[24nov.2025 23:48:54.997] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/biome_source
-[24nov.2025 23:48:54.997] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/chunk_generator
-[24nov.2025 23:48:54.997] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/chunk_generator
-[24nov.2025 23:48:54.997] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/material_condition
-[24nov.2025 23:48:54.997] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/material_condition
-[24nov.2025 23:48:54.999] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/material_rule
-[24nov.2025 23:48:55.000] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/material_rule
-[24nov.2025 23:48:55.011] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/density_function_type
-[24nov.2025 23:48:55.012] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/density_function_type
-[24nov.2025 23:48:55.012] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_pool_element
-[24nov.2025 23:48:55.012] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_pool_element
-[24nov.2025 23:48:55.012] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:cat_variant
-[24nov.2025 23:48:55.012] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:cat_variant
-[24nov.2025 23:48:55.012] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:frog_variant
-[24nov.2025 23:48:55.012] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:frog_variant
-[24nov.2025 23:48:55.012] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:banner_pattern
-[24nov.2025 23:48:55.013] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:banner_pattern
-[24nov.2025 23:48:55.013] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:instrument
-[24nov.2025 23:48:55.013] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:instrument
-[24nov.2025 23:48:55.013] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:decorated_pot_patterns
-[24nov.2025 23:48:55.013] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:decorated_pot_patterns
-[24nov.2025 23:48:55.015] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:creative_mode_tab
-[24nov.2025 23:48:55.015] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:creative_mode_tab
-[24nov.2025 23:48:55.015] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: betterdays:time_effect
-[24nov.2025 23:48:55.016] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: betterdays:time_effect
-[24nov.2025 23:48:55.027] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:biome_modifier_serializers
-[24nov.2025 23:48:55.027] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:biome_modifier_serializers
-[24nov.2025 23:48:55.028] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:display_contexts
-[24nov.2025 23:48:55.028] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:display_contexts
-[24nov.2025 23:48:55.028] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:entity_data_serializers
-[24nov.2025 23:48:55.028] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:entity_data_serializers
-[24nov.2025 23:48:55.035] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:fluid_type
-[24nov.2025 23:48:55.035] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:fluid_type
-[24nov.2025 23:48:55.035] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:global_loot_modifier_serializers
-[24nov.2025 23:48:55.035] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:global_loot_modifier_serializers
-[24nov.2025 23:48:55.043] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:holder_set_type
-[24nov.2025 23:48:55.044] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:holder_set_type
-[24nov.2025 23:48:55.044] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:structure_modifier_serializers
-[24nov.2025 23:48:55.045] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:structure_modifier_serializers
-[24nov.2025 23:48:55.046] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: lithostitched:modifier_predicate_type
-[24nov.2025 23:48:55.048] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: lithostitched:modifier_predicate_type
-[24nov.2025 23:48:55.052] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: lithostitched:modifier_type
-[24nov.2025 23:48:55.053] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: lithostitched:modifier_type
-[24nov.2025 23:48:55.053] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: lithostitched:placement_condition_type
-[24nov.2025 23:48:55.053] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: lithostitched:placement_condition_type
-[24nov.2025 23:48:55.053] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: lithostitched:processor_condition_type
-[24nov.2025 23:48:55.055] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: lithostitched:processor_condition_type
-[24nov.2025 23:48:55.055] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/biome
-[24nov.2025 23:48:55.055] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/biome
-[24nov.2025 23:48:55.151] [main/DEBUG] [mixin/]: Mixing common.ServerLifecycleHooksMixin from lithostitched.forge.mixins.json into net.minecraftforge.server.ServerLifecycleHooks
-[24nov.2025 23:48:55.151] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$lithostitched$injectBiomeModifers$1(Ljava/util/List;Ljava/util/Map$Entry;)V to mdd47aa8$lambda$lithostitched$injectBiomeModifers$1$0 in lithostitched.forge.mixins.json:common.ServerLifecycleHooksMixin
-[24nov.2025 23:48:55.151] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$lithostitched$injectBiomeModifers$0(Ljava/util/Map$Entry;)Z to mdd47aa8$lambda$lithostitched$injectBiomeModifers$0$1 in lithostitched.forge.mixins.json:common.ServerLifecycleHooksMixin
-[24nov.2025 23:48:55.188] [main/DEBUG] [mixin/]: Mixing BuiltInPackSourceAccessor from tectonic_1.20.1.mixins.json into net.minecraft.server.packs.repository.BuiltInPackSource
-[24nov.2025 23:48:55.197] [main/WARN] [net.minecraft.server.packs.VanillaPackResourcesBuilder/]: Assets URL 'union:/C:/Users/matga/.gradle/caches/forge_gradle/minecraft_user_repo/net/minecraftforge/forge/1.20.1-47.4.9_mapped_official_1.20.1/forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar%23191!/assets/.mcassetsroot' uses unexpected schema
-[24nov.2025 23:48:55.197] [main/WARN] [net.minecraft.server.packs.VanillaPackResourcesBuilder/]: Assets URL 'union:/C:/Users/matga/.gradle/caches/forge_gradle/minecraft_user_repo/net/minecraftforge/forge/1.20.1-47.4.9_mapped_official_1.20.1/forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar%23191!/data/.mcassetsroot' uses unexpected schema
-[24nov.2025 23:48:55.412] [main/INFO] [net.minecraft.data.DataGenerator/]: All providers took: 0 ms
-[24nov.2025 23:48:55.414] [main/INFO] [net.minecraft.data.HashCache/]: Caching: total files: 0, old count: 0, new count: 1, removed stale: 0, written: 0
+[12mai2026 16:10:31.377] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for embeddium
+[12mai2026 16:10:31.377] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing org.embeddedt.embeddium.render.frapi.SpriteFinderCache to MOD
+[12mai2026 16:10:31.380] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.model.colors.ItemColorsMixin from embeddium.mixins.json into net.minecraft.client.color.item.ItemColors
+[12mai2026 16:10:31.381] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.HideNametag to FORGE
+[12mai2026 16:10:31.387] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.textures.animations.tracking.SpriteMixin from embeddium.mixins.json into net.minecraft.client.renderer.texture.TextureAtlasSprite
+[12mai2026 16:10:31.397] [modloading-worker-0/DEBUG] [mixin/]: Mixing NameTagToggleMixin from chloride.mixin.json into net.minecraft.client.renderer.entity.EntityRenderer
+[12mai2026 16:10:31.397] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.render.entity.cull.EntityRendererMixin from embeddium.mixins.json into net.minecraft.client.renderer.entity.EntityRenderer
+[12mai2026 16:10:31.400] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for forge
+[12mai2026 16:10:31.400] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.common.ForgeSpawnEggItem$CommonHandler to MOD
+[12mai2026 16:10:31.404] [modloading-worker-0/DEBUG] [mixin/]: Mixing core.model.colors.BlockColorsMixin from embeddium.mixins.json into net.minecraft.client.color.block.BlockColors
+[12mai2026 16:10:31.404] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing org.embeddedt.embeddium.impl.ConfigScreenHandlerHook to MOD
+[12mai2026 16:10:31.406] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file simpleclouds-client.toml for simpleclouds tracking
+[12mai2026 16:10:31.406] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file simpleclouds-common.toml for simpleclouds tracking
+[12mai2026 16:10:31.406] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.config.ConfigTracker/CONFIG]: Config file simpleclouds-server.toml for simpleclouds tracking
+[12mai2026 16:10:31.412] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.FastBlocks to FORGE
+[12mai2026 16:10:31.415] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.common.ForgeSpawnEggItem$ColorRegisterHandler to MOD
+[12mai2026 16:10:31.415] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing org.embeddedt.embeddium.compat.immersive.ImmersiveCompat to MOD
+[12mai2026 16:10:31.417] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing org.embeddedt.embeddium.compat.ccl.CCLCompatBootstrap to MOD
+[12mai2026 16:10:31.418] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.impl.FastBlocks$ModEvents to MOD
+[12mai2026 16:10:31.418] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for simpleclouds
+[12mai2026 16:10:31.418] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.client.model.data.ModelDataManager to FORGE
+[12mai2026 16:10:31.420] [modloading-worker-0/DEBUG] [WhiteNoise/CONFIG]: Config file betterdays-client.toml for betterdays added to tracking
+[12mai2026 16:10:31.421] [modloading-worker-0/DEBUG] [WhiteNoise/CONFIG]: Config file betterdays-common.toml for betterdays added to tracking
+[12mai2026 16:10:31.421] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for betterdays
+[12mai2026 16:10:31.422] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.model.ModelDataMixin from embeddium.mixins.json into net.minecraftforge.client.model.data.ModelData
+[12mai2026 16:10:31.428] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.ChlorideConfig to MOD
+[12mai2026 16:10:31.429] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.client.ForgeHooksClient$ClientEvents to MOD
+[12mai2026 16:10:31.433] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.shader.uniform.ShaderProgramMixin from embeddium.mixins.json into net.minecraft.client.renderer.ShaderInstance
+[12mai2026 16:10:31.433] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for sereneseasons
+[12mai2026 16:10:31.433] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing sereneseasons.forge.datagen.DataGenerationHandler to MOD
+[12mai2026 16:10:31.452] [modloading-worker-0/DEBUG] [mixin/]: Mixing FontShadowMixin from chloride.mixin.json into net.minecraft.client.gui.Font
+[12mai2026 16:10:31.463] [modloading-worker-0/DEBUG] [mixin/]: Mixing CloudGeneratorHurricaneReservationMixin from projectatmosphere.mixins.json into dev.nonamecrackers2.simpleclouds.common.cloud.spawning.CloudGenerator
+[12mai2026 16:10:31.469] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing me.srrapero720.chloride.Chloride to MOD
+[12mai2026 16:10:31.478] [modloading-worker-0/DEBUG] [mixin/]: Mixing features.textures.animations.tracking.SpriteAtlasTextureMixin from embeddium.mixins.json into net.minecraft.client.renderer.texture.TextureAtlas
+[12mai2026 16:10:31.484] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.minecraftforge.client.ClientForgeMod to MOD
+[12mai2026 16:10:31.489] [modloading-worker-0/INFO] [ProjectAtmosphere/Seasons/]: Detected Serene Seasons; using SereneSeasonsSeasonDelegate.
+[12mai2026 16:10:31.496] [modloading-worker-0/INFO] [ProjectAtmosphere/SeasonTime/]: Season time delegate set to 'net.Gabou.projectatmosphere.seasons.SereneSeasonsSeasonDelegate'.
+[12mai2026 16:10:31.496] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for projectatmosphere
+[12mai2026 16:10:31.496] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.HUDOverlayRenderer to FORGE
+[12mai2026 16:10:31.499] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.loading.ClientForecastLoadingLifecycle to FORGE
+[12mai2026 16:10:31.502] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for tectonic
+[12mai2026 16:10:31.502] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.loading.ClientForecastLoadingWorkQueue to FORGE
+[12mai2026 16:10:31.502] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.worldgen.tectonic.TectonicLexforge$ModBusEvents to MOD
+[12mai2026 16:10:31.505] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.render.HudRenderTest to FORGE
+[12mai2026 16:10:31.506] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.render.HurricaneShaders to MOD
+[12mai2026 16:10:31.508] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.render.SimpleCloudsDhPipelineSelector to FORGE
+[12mai2026 16:10:31.512] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.render.TornadoShaders to MOD
+[12mai2026 16:10:31.514] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.client.SimpleCloudsWhiteoutFogHandler to FORGE
+[12mai2026 16:10:31.522] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.command.CloudDumpCommand to FORGE
+[12mai2026 16:10:31.525] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.command.TelemetryDebugClientCommand to FORGE
+[12mai2026 16:10:31.527] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.event.BiomeChangeManager to FORGE
+[12mai2026 16:10:31.528] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.event.EventHandler to FORGE
+[12mai2026 16:10:31.528] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.event.SimpleCloudsEventListener to FORGE
+[12mai2026 16:10:31.531] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Loading config file: G:\Project-Atmosphere\run-data\config\jei\jei-debug.ini
+[12mai2026 16:10:31.532] [modloading-worker-0/DEBUG] [mixin/]: Mixing CloudRegionTickEventMixin from projectatmosphere.mixins.json into dev.nonamecrackers2.simpleclouds.api.common.event.CloudRegionTickEvent
+[12mai2026 16:10:31.534] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Saving config file: G:\Project-Atmosphere\run-data\config\jei\jei-debug.ini
+[12mai2026 16:10:31.545] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.event.TemperatureTickHandler to FORGE
+[12mai2026 16:10:31.545] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.modules.weather.StormShieldManager to FORGE
+[12mai2026 16:10:31.548] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Loading config file: G:\Project-Atmosphere\run-data\config\jei\jei-mod-id-format.ini
+[12mai2026 16:10:31.550] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Saving config file: G:\Project-Atmosphere\run-data\config\jei\jei-mod-id-format.ini
+[12mai2026 16:10:31.551] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.ProjectAtmosphere to FORGE
+[12mai2026 16:10:31.555] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.registry.ModClient to MOD
+[12mai2026 16:10:31.557] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.telemetry.ServerTelemetrySampler to FORGE
+[12mai2026 16:10:31.558] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Loading config file: G:\Project-Atmosphere\run-data\config\jei\jei-colors.ini
+[12mai2026 16:10:31.562] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.util.CloudSpawnScheduler to FORGE
+[12mai2026 16:10:31.562] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Saving config file: G:\Project-Atmosphere\run-data\config\jei\jei-colors.ini
+[12mai2026 16:10:31.564] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing net.Gabou.projectatmosphere.util.ParticleAtlasDebugger to FORGE
+[12mai2026 16:10:31.568] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for lithostitched
+[12mai2026 16:10:31.577] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for architectury
+[12mai2026 16:10:31.577] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.level.entity.trade.forge.TradeRegistryImpl to FORGE
+[12mai2026 16:10:31.579] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.forge.ReloadListenerRegistryImpl to FORGE
+[12mai2026 16:10:31.579] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Loading config file: G:\Project-Atmosphere\run-data\config\jei\jei-client.ini
+[12mai2026 16:10:31.580] [modloading-worker-0/DEBUG] [mezz.jei.common.config.file.ConfigSerializer/]: Saving config file: G:\Project-Atmosphere\run-data\config\jei\jei-client.ini
+[12mai2026 16:10:31.580] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.forge.CreativeTabRegistryImpl to MOD
+[12mai2026 16:10:31.590] [modloading-worker-0/INFO] [mezz.jei.library.load.PluginCaller/]: Sending ConfigManager...
+[12mai2026 16:10:31.593] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.client.particle.forge.ParticleProviderRegistryImpl to MOD
+[12mai2026 16:10:31.597] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.registry.client.keymappings.forge.KeyMappingRegistryImpl to MOD
+[12mai2026 16:10:31.598] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:minecraft...
+[12mai2026 16:10:31.598] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jade:main...
+[12mai2026 16:10:31.598] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:debug...
+[12mai2026 16:10:31.598] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:gui...
+[12mai2026 16:10:31.598] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:forge_gui...
+[12mai2026 16:10:31.598] [modloading-worker-0/DEBUG] [mezz.jei.library.load.PluginCallerTimerRunnable/]: Sending ConfigManager: jei:internal...
+[12mai2026 16:10:31.598] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Auto-subscribing dev.architectury.networking.forge.NetworkManagerImpl to FORGE
+[12mai2026 16:10:31.600] [modloading-worker-0/INFO] [mezz.jei.library.load.PluginCaller/]: Sending ConfigManager took 6.169 ms
+[12mai2026 16:10:31.609] [modloading-worker-0/DEBUG] [net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber/LOADING]: Attempting to inject @EventBusSubscriber classes into the eventbus for jei
+[12mai2026 16:10:31.610] [modloading-worker-0/INFO] [dev.architectury.networking.forge.NetworkManagerImpl/]: Registering S2C receiver with id architectury:sync_ids
+[12mai2026 16:10:31.613] [modloading-worker-0/INFO] [dev.architectury.networking.forge.NetworkManagerImpl/]: Registering C2S receiver with id architectury:sync_ids
+[12mai2026 16:10:31.624] [main/DEBUG] [WhiteNoise/CONFIG]: Loading global configs
+[12mai2026 16:10:31.626] [main/DEBUG] [WhiteNoise/CONFIG]: Built TOML config for G:\Project-Atmosphere\run-data\config\betterdays-client.toml
+[12mai2026 16:10:31.626] [main/DEBUG] [WhiteNoise/CONFIG]: Loaded TOML config file G:\Project-Atmosphere\run-data\config\betterdays-client.toml
+[12mai2026 16:10:31.630] [main/DEBUG] [WhiteNoise/CONFIG]: Built TOML config for G:\Project-Atmosphere\run-data\config\betterdays-common.toml
+[12mai2026 16:10:31.631] [main/DEBUG] [WhiteNoise/CONFIG]: Loaded TOML config file G:\Project-Atmosphere\run-data\config\betterdays-common.toml
+[12mai2026 16:10:31.639] [main/DEBUG] [mixin/]: Mixing common.RegistryDataLoaderMixin from lithostitched.mixins.json into net.minecraft.resources.RegistryDataLoader
+[12mai2026 16:10:31.663] [main/DEBUG] [net.minecraftforge.registries.ObjectHolderRegistry/REGISTRIES]: Processing ObjectHolder annotations
+[12mai2026 16:10:31.674] [main/DEBUG] [net.minecraftforge.registries.ObjectHolderRegistry/REGISTRIES]: Found 3936 ObjectHolder annotations
+[12mai2026 16:10:31.678] [main/DEBUG] [net.minecraftforge.common.capabilities.CapabilityManager/CAPABILITIES]: Attempting to automatically register: Lnet/minecraftforge/energy/IEnergyStorage;
+[12mai2026 16:10:31.678] [main/DEBUG] [net.minecraftforge.common.capabilities.CapabilityManager/CAPABILITIES]: Attempting to automatically register: Lnet/minecraftforge/fluids/capability/IFluidHandler;
+[12mai2026 16:10:31.679] [main/DEBUG] [net.minecraftforge.common.capabilities.CapabilityManager/CAPABILITIES]: Attempting to automatically register: Lnet/minecraftforge/fluids/capability/IFluidHandlerItem;
+[12mai2026 16:10:31.679] [main/DEBUG] [net.minecraftforge.common.capabilities.CapabilityManager/CAPABILITIES]: Attempting to automatically register: Lnet/minecraftforge/items/IItemHandler;
+[12mai2026 16:10:31.679] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Unfreezing vanilla registries
+[12mai2026 16:10:31.774] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:sound_event
+[12mai2026 16:10:31.777] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:sound_event
+[12mai2026 16:10:31.778] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:fluid
+[12mai2026 16:10:31.778] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:fluid
+[12mai2026 16:10:31.787] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:block
+[12mai2026 16:10:31.787] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:block
+[12mai2026 16:10:31.788] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:attribute
+[12mai2026 16:10:31.789] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:attribute
+[12mai2026 16:10:31.789] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:mob_effect
+[12mai2026 16:10:31.789] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:mob_effect
+[12mai2026 16:10:31.793] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:particle_type
+[12mai2026 16:10:31.795] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:particle_type
+[12mai2026 16:10:31.822] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:item
+[12mai2026 16:10:31.824] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:item
+[12mai2026 16:10:31.824] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:entity_type
+[12mai2026 16:10:31.825] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:entity_type
+[12mai2026 16:10:31.825] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:sensor_type
+[12mai2026 16:10:31.825] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:sensor_type
+[12mai2026 16:10:31.825] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:memory_module_type
+[12mai2026 16:10:31.825] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:memory_module_type
+[12mai2026 16:10:31.825] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:potion
+[12mai2026 16:10:31.825] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:potion
+[12mai2026 16:10:31.825] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:game_event
+[12mai2026 16:10:31.825] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:game_event
+[12mai2026 16:10:31.826] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:enchantment
+[12mai2026 16:10:31.826] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:enchantment
+[12mai2026 16:10:31.828] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:block_entity_type
+[12mai2026 16:10:31.828] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:block_entity_type
+[12mai2026 16:10:31.828] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:painting_variant
+[12mai2026 16:10:31.828] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:painting_variant
+[12mai2026 16:10:31.829] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:stat_type
+[12mai2026 16:10:31.829] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:stat_type
+[12mai2026 16:10:31.829] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:custom_stat
+[12mai2026 16:10:31.829] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:custom_stat
+[12mai2026 16:10:31.829] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:chunk_status
+[12mai2026 16:10:31.829] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:chunk_status
+[12mai2026 16:10:31.829] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:rule_test
+[12mai2026 16:10:31.829] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:rule_test
+[12mai2026 16:10:31.831] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:rule_block_entity_modifier
+[12mai2026 16:10:31.831] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:rule_block_entity_modifier
+[12mai2026 16:10:31.831] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:pos_rule_test
+[12mai2026 16:10:31.831] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:pos_rule_test
+[12mai2026 16:10:31.831] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:menu
+[12mai2026 16:10:31.832] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:menu
+[12mai2026 16:10:31.832] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:recipe_type
+[12mai2026 16:10:31.832] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:recipe_type
+[12mai2026 16:10:31.875] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:recipe_serializer
+[12mai2026 16:10:31.875] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:recipe_serializer
+[12mai2026 16:10:31.875] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:position_source_type
+[12mai2026 16:10:31.876] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:position_source_type
+[12mai2026 16:10:31.878] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:command_argument_type
+[12mai2026 16:10:31.879] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:command_argument_type
+[12mai2026 16:10:31.879] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:villager_type
+[12mai2026 16:10:31.879] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:villager_type
+[12mai2026 16:10:31.879] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:villager_profession
+[12mai2026 16:10:31.879] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:villager_profession
+[12mai2026 16:10:31.880] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:point_of_interest_type
+[12mai2026 16:10:31.880] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:point_of_interest_type
+[12mai2026 16:10:31.880] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:schedule
+[12mai2026 16:10:31.880] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:schedule
+[12mai2026 16:10:31.880] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:activity
+[12mai2026 16:10:31.880] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:activity
+[12mai2026 16:10:31.880] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_pool_entry_type
+[12mai2026 16:10:31.880] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_pool_entry_type
+[12mai2026 16:10:31.880] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_function_type
+[12mai2026 16:10:31.881] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_function_type
+[12mai2026 16:10:31.883] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_condition_type
+[12mai2026 16:10:31.883] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_condition_type
+[12mai2026 16:10:31.883] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_number_provider_type
+[12mai2026 16:10:31.884] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_number_provider_type
+[12mai2026 16:10:31.884] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_nbt_provider_type
+[12mai2026 16:10:31.884] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_nbt_provider_type
+[12mai2026 16:10:31.884] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:loot_score_provider_type
+[12mai2026 16:10:31.884] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:loot_score_provider_type
+[12mai2026 16:10:31.884] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:float_provider_type
+[12mai2026 16:10:31.885] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:float_provider_type
+[12mai2026 16:10:31.885] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:int_provider_type
+[12mai2026 16:10:31.885] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:int_provider_type
+[12mai2026 16:10:31.885] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:height_provider_type
+[12mai2026 16:10:31.886] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:height_provider_type
+[12mai2026 16:10:31.886] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:block_predicate_type
+[12mai2026 16:10:31.886] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:block_predicate_type
+[12mai2026 16:10:31.886] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/carver
+[12mai2026 16:10:31.886] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/carver
+[12mai2026 16:10:31.886] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/feature
+[12mai2026 16:10:31.886] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/feature
+[12mai2026 16:10:31.886] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_processor
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_processor
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_placement
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_placement
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_piece
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_piece
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_type
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_type
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/placement_modifier_type
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/placement_modifier_type
+[12mai2026 16:10:31.887] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/block_state_provider_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/block_state_provider_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/foliage_placer_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/foliage_placer_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/trunk_placer_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/trunk_placer_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/root_placer_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/root_placer_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/tree_decorator_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/tree_decorator_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/feature_size_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/feature_size_type
+[12mai2026 16:10:31.889] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/biome_source
+[12mai2026 16:10:31.890] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/biome_source
+[12mai2026 16:10:31.890] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/chunk_generator
+[12mai2026 16:10:31.890] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/chunk_generator
+[12mai2026 16:10:31.890] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/material_condition
+[12mai2026 16:10:31.890] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/material_condition
+[12mai2026 16:10:31.891] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/material_rule
+[12mai2026 16:10:31.892] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/material_rule
+[12mai2026 16:10:31.893] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/density_function_type
+[12mai2026 16:10:31.893] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/density_function_type
+[12mai2026 16:10:31.894] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/structure_pool_element
+[12mai2026 16:10:31.894] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/structure_pool_element
+[12mai2026 16:10:31.894] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:cat_variant
+[12mai2026 16:10:31.895] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:cat_variant
+[12mai2026 16:10:31.895] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:frog_variant
+[12mai2026 16:10:31.895] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:frog_variant
+[12mai2026 16:10:31.895] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:banner_pattern
+[12mai2026 16:10:31.895] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:banner_pattern
+[12mai2026 16:10:31.895] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:instrument
+[12mai2026 16:10:31.895] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:instrument
+[12mai2026 16:10:31.895] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:decorated_pot_patterns
+[12mai2026 16:10:31.895] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:decorated_pot_patterns
+[12mai2026 16:10:31.897] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:creative_mode_tab
+[12mai2026 16:10:31.897] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:creative_mode_tab
+[12mai2026 16:10:31.898] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: betterdays:time_effect
+[12mai2026 16:10:31.898] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: betterdays:time_effect
+[12mai2026 16:10:31.908] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:biome_modifier_serializers
+[12mai2026 16:10:31.909] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:biome_modifier_serializers
+[12mai2026 16:10:31.910] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:display_contexts
+[12mai2026 16:10:31.910] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:display_contexts
+[12mai2026 16:10:31.910] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:entity_data_serializers
+[12mai2026 16:10:31.910] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:entity_data_serializers
+[12mai2026 16:10:31.915] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:fluid_type
+[12mai2026 16:10:31.915] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:fluid_type
+[12mai2026 16:10:31.915] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:global_loot_modifier_serializers
+[12mai2026 16:10:31.916] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:global_loot_modifier_serializers
+[12mai2026 16:10:31.925] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:holder_set_type
+[12mai2026 16:10:31.925] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:holder_set_type
+[12mai2026 16:10:31.926] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: forge:structure_modifier_serializers
+[12mai2026 16:10:31.926] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: forge:structure_modifier_serializers
+[12mai2026 16:10:31.928] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: lithostitched:modifier_predicate_type
+[12mai2026 16:10:31.928] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: lithostitched:modifier_predicate_type
+[12mai2026 16:10:31.932] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: lithostitched:modifier_type
+[12mai2026 16:10:31.933] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: lithostitched:modifier_type
+[12mai2026 16:10:31.933] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: lithostitched:placement_condition_type
+[12mai2026 16:10:31.934] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: lithostitched:placement_condition_type
+[12mai2026 16:10:31.934] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: lithostitched:processor_condition_type
+[12mai2026 16:10:31.934] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: lithostitched:processor_condition_type
+[12mai2026 16:10:31.934] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Applying holder lookups: minecraft:worldgen/biome
+[12mai2026 16:10:31.935] [main/DEBUG] [net.minecraftforge.registries.GameData/REGISTRIES]: Holder lookups applied: minecraft:worldgen/biome
+[12mai2026 16:10:31.969] [main/DEBUG] [mixin/]: Mixing common.ServerLifecycleHooksMixin from lithostitched.forge.mixins.json into net.minecraftforge.server.ServerLifecycleHooks
+[12mai2026 16:10:31.969] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$lithostitched$injectBiomeModifers$1(Ljava/util/List;Ljava/util/Map$Entry;)V to mdb27d7e$lambda$lithostitched$injectBiomeModifers$1$0 in lithostitched.forge.mixins.json:common.ServerLifecycleHooksMixin
+[12mai2026 16:10:31.970] [main/DEBUG] [mixin/]: Renaming synthetic method lambda$lithostitched$injectBiomeModifers$0(Ljava/util/Map$Entry;)Z to mdb27d7e$lambda$lithostitched$injectBiomeModifers$0$1 in lithostitched.forge.mixins.json:common.ServerLifecycleHooksMixin
+[12mai2026 16:10:32.018] [main/DEBUG] [mixin/]: Mixing BuiltInPackSourceAccessor from tectonic_1.20.1.mixins.json into net.minecraft.server.packs.repository.BuiltInPackSource
+[12mai2026 16:10:32.029] [main/WARN] [net.minecraft.server.packs.VanillaPackResourcesBuilder/]: Assets URL 'union:/C:/Users/matga/.gradle/caches/forge_gradle/minecraft_user_repo/net/minecraftforge/forge/1.20.1-47.4.13_mapped_official_1.20.1/forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar%23191!/assets/.mcassetsroot' uses unexpected schema
+[12mai2026 16:10:32.030] [main/WARN] [net.minecraft.server.packs.VanillaPackResourcesBuilder/]: Assets URL 'union:/C:/Users/matga/.gradle/caches/forge_gradle/minecraft_user_repo/net/minecraftforge/forge/1.20.1-47.4.13_mapped_official_1.20.1/forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar%23191!/data/.mcassetsroot' uses unexpected schema
+[12mai2026 16:10:32.274] [Worker-Main-1/DEBUG] [mixin/]: Mixing NoisesMixin from tectonic.mixins.json into net.minecraft.world.level.levelgen.Noises
+[12mai2026 16:10:32.300] [main/INFO] [net.minecraft.data.DataGenerator/]: All providers took: 0 ms
+[12mai2026 16:10:32.304] [main/INFO] [net.minecraft.data.HashCache/]: Caching: total files: 0, old count: 0, new count: 1, removed stale: 0, written: 0
diff --git a/run-data/logs/latest.log b/run-data/logs/latest.log
index b716fb1a..0937b979 100644
--- a/run-data/logs/latest.log
+++ b/run-data/logs/latest.log
@@ -1,69 +1,69 @@
-[24nov.2025 23:48:47.382] [main/INFO] [cpw.mods.modlauncher.Launcher/MODLAUNCHER]: ModLauncher running: args [--launchTarget, forgedatauserdev, --assetIndex, 5, --assetsDir, C:\Users\matga\.gradle\caches\forge_gradle\assets, --gameDir, ., --fml.forgeVersion, 47.4.9, --fml.mcVersion, 1.20.1, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20230612.114412, --mod, projectatmosphere, --all, --output, G:\Project-Atmosphere\src\generated\resources, --existing, G:\Project-Atmosphere\src\main\resources, --mixin.config, projectatmosphere.mixins.json]
-[24nov.2025 23:48:47.390] [main/INFO] [cpw.mods.modlauncher.Launcher/MODLAUNCHER]: ModLauncher 10.0.9+10.0.9+main.dcd20f30 starting: java version 17.0.15 by Eclipse Adoptium; OS Windows 11 arch amd64 version 10.0
-[24nov.2025 23:48:47.503] [main/INFO] [net.minecraftforge.fml.loading.ImmediateWindowHandler/]: ImmediateWindowProvider not loading because launch target is forgedatauserdev
-[24nov.2025 23:48:47.534] [main/INFO] [mixin/]: SpongePowered MIXIN Subsystem Version=0.8.5 Source=union:/C:/Users/matga/.gradle/caches/modules-2/files-2.1/org.spongepowered/mixin/0.8.5/9d1c0c3a304ae6697ecd477218fa61b850bf57fc/mixin-0.8.5.jar%23132!/ Service=ModLauncher Env=CLIENT
-[24nov.2025 23:48:47.788] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\javafmllanguage\1.20.1-47.4.9\6802c410879d06cd2c16ac24988dd5893293e21c\javafmllanguage-1.20.1-47.4.9.jar is missing mods.toml file
-[24nov.2025 23:48:47.790] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\lowcodelanguage\1.20.1-47.4.9\1a098871d069f3cb2ed12f392f96f462902c0350\lowcodelanguage-1.20.1-47.4.9.jar is missing mods.toml file
-[24nov.2025 23:48:47.792] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\mclanguage\1.20.1-47.4.9\5279b751da7297817db4fa9729e3f3c7bf03d594\mclanguage-1.20.1-47.4.9.jar is missing mods.toml file
-[24nov.2025 23:48:47.795] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\fmlcore\1.20.1-47.4.9\f4f03c369d155fb551ebf908db260a0c5600c29b\fmlcore-1.20.1-47.4.9.jar is missing mods.toml file
-[24nov.2025 23:48:48.031] [main/INFO] [net.minecraftforge.fml.loading.moddiscovery.JarInJarDependencyLocator/]: Found 3 dependencies adding them to mods collection
-[24nov.2025 23:48:49.314] [main/INFO] [mixin/]: Compatibility level set to JAVA_17
-[24nov.2025 23:48:49.314] [main/ERROR] [mixin/]: Mixin config projectatmosphere.mixins.json does not specify "minVersion" property
-[24nov.2025 23:48:49.336] [main/INFO] [cpw.mods.modlauncher.LaunchServiceHandler/MODLAUNCHER]: Launching target 'forgedatauserdev' with arguments [--gameDir, ., --assetsDir, C:\Users\matga\.gradle\caches\forge_gradle\assets, --assetIndex, 5, --mod, projectatmosphere, --all, --output, G:\Project-Atmosphere\src\generated\resources, --existing, G:\Project-Atmosphere\src\main\resources]
-[24nov.2025 23:48:49.453] [main/WARN] [mixin/]: Reference map 'projectatmosphere.refmap.json' for projectatmosphere.mixins.json could not be read. If this is a development environment you can ignore this message
-[24nov.2025 23:48:49.600] [main/INFO] [mixin/]: Remapping refMap jade.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.601] [main/INFO] [mixin/]: Remapping refMap lithostitched.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.602] [main/INFO] [mixin/]: Remapping refMap lithostitched.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.602] [main/INFO] [mixin/]: Remapping refMap sereneseasonsplus-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.602] [main/INFO] [mixin/]: Remapping refMap crackerslib.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.705] [main/INFO] [mixin/]: Remapping refMap glitchcore.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.705] [main/INFO] [mixin/]: Remapping refMap glitchcore.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.706] [main/INFO] [mixin/]: Remapping refMap sereneseasons.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.706] [main/INFO] [mixin/]: Remapping refMap sereneseasons.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.707] [main/INFO] [mixin/]: Remapping refMap simpleclouds.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.710] [main/INFO] [mixin/]: Remapping refMap architectury-forge-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.710] [main/INFO] [mixin/]: Remapping refMap architectury-common-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.711] [main/INFO] [mixin/]: Remapping refMap auroras.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.712] [main/WARN] [mixin/]: Reference map 'rainbows.refmap.json' for rainbows.mixins.json could not be read. If this is a development environment you can ignore this message
-[24nov.2025 23:48:49.720] [main/INFO] [Embeddium/]: Loaded configuration file for Embeddium: 68 options available, 0 override(s) found
-[24nov.2025 23:48:49.722] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Searching for graphics cards...
-[24nov.2025 23:48:49.963] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Found graphics card: GraphicsAdapterInfo[vendor=UNKNOWN, name=Meta Virtual Monitor, version=DriverVersion=17.12.55.198]
-[24nov.2025 23:48:49.963] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Found graphics card: GraphicsAdapterInfo[vendor=NVIDIA, name=NVIDIA GeForce RTX 4070 Ti SUPER, version=DriverVersion=32.0.15.8180]
-[24nov.2025 23:48:49.967] [main/WARN] [Embeddium-Workarounds/]: Embeddium has applied one or more workarounds to prevent crashes or other issues on your system: [NVIDIA_THREADED_OPTIMIZATIONS]
-[24nov.2025 23:48:49.967] [main/WARN] [Embeddium-Workarounds/]: This is not necessarily an issue, but it may result in certain features or optimizations being disabled. You can sometimes fix these issues by upgrading your graphics driver.
-[24nov.2025 23:48:49.969] [main/INFO] [mixin/]: Remapping refMap embeddium-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:49.971] [main/INFO] [mixin/]: Remapping refMap chloride.mixin-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
-[24nov.2025 23:48:50.267] [main/WARN] [mixin/]: Error loading class: org/vivecraft/client_vr/provider/VRRenderer (java.lang.ClassNotFoundException: org.vivecraft.client_vr.provider.VRRenderer)
-[24nov.2025 23:48:50.301] [main/WARN] [mixin/]: Error loading class: com/aetherteam/aether/client/renderer/level/AetherSkyRenderEffects (java.lang.ClassNotFoundException: com.aetherteam.aether.client.renderer.level.AetherSkyRenderEffects)
-[24nov.2025 23:48:50.301] [main/WARN] [mixin/]: @Mixin target com.aetherteam.aether.client.renderer.level.AetherSkyRenderEffects was not found auroras.mixins.json:client.AetherSkyRenderEffectsMixin
-[24nov.2025 23:48:50.315] [main/WARN] [mixin/]: Error loading class: dev/emi/emi/screen/EmiScreenManager (java.lang.ClassNotFoundException: dev.emi.emi.screen.EmiScreenManager)
-[24nov.2025 23:48:50.317] [main/WARN] [mixin/]: Error loading class: me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl (java.lang.ClassNotFoundException: me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl)
-[24nov.2025 23:48:50.741] [main/INFO] [net.minecraftforge.data.loading.DatagenModLoader/]: Initializing Data Gatherer for mods [projectatmosphere]
-[24nov.2025 23:48:50.752] [main/INFO] [MixinExtras|Service/]: Initializing MixinExtras via com.llamalad7.mixinextras.service.MixinExtrasServiceImpl(version=0.3.5).
-[24nov.2025 23:48:51.426] [main/WARN] [mixin/]: Method overwrite conflict for skipRendering in embeddium.mixins.json:features.options.render_layers.LeavesBlockMixin, previously written by me.srrapero720.chloride.mixins.impl.leaves_culling.LeavesBlockMixin. Skipping method.
-[24nov.2025 23:48:54.017] [modloading-worker-0/INFO] [projectatmosphere/]: Project Atmosphere is loading!
-[24nov.2025 23:48:54.024] [modloading-worker-0/INFO] [chloride/Main]: Chloride is here, lets make your experience taste-able
-[24nov.2025 23:48:54.171] [modloading-worker-0/INFO] [projectatmosphere/]: No temperature mod loaded, skipping compatibility setup.
-[24nov.2025 23:48:54.171] [modloading-worker-0/INFO] [projectatmosphere/]: Sand Storms mod not found.
-[24nov.2025 23:48:54.174] [modloading-worker-0/INFO] [net.minecraftforge.common.ForgeMod/FORGEMOD]: Forge mod loading, version 47.4.9, for MC 1.20.1 with MCP 20230612.114412
-[24nov.2025 23:48:54.171] [modloading-worker-0/INFO] [projectatmosphere/]: Auroras detected – enabling seasonal aurora tuning.
-[24nov.2025 23:48:54.176] [modloading-worker-0/INFO] [net.minecraftforge.common.MinecraftForge/FORGE]: MinecraftForge v47.4.9 Initialized
-[24nov.2025 23:48:54.175] [modloading-worker-0/INFO] [projectatmosphere/]: Rainbows detected – enabling precipitation bridge.
-[24nov.2025 23:48:54.176] [modloading-worker-0/INFO] [projectatmosphere/]: Tectonic detected – enabling refined ocean geometry.
-[24nov.2025 23:48:54.178] [modloading-worker-0/INFO] [projectatmosphere/]: Continents mod not detected.
-[24nov.2025 23:48:54.401] [modloading-worker-0/WARN] [mixin/]: Static binding violation: PRIVATE @Overwrite method setSectionDirty in embeddium.mixins.json:core.render.world.WorldRendererMixin cannot reduce visibiliy of PUBLIC target method, visibility will be upgraded.
-[24nov.2025 23:48:54.610] [modloading-worker-0/ERROR] [Embeddium/]: Failed to update fingerprint
+[12mai2026 16:10:21.587] [main/INFO] [cpw.mods.modlauncher.Launcher/MODLAUNCHER]: ModLauncher running: args [--launchTarget, forgedatauserdev, --assetIndex, 5, --assetsDir, C:\Users\matga\.gradle\caches\forge_gradle\assets, --gameDir, ., --fml.forgeVersion, 47.4.13, --fml.mcVersion, 1.20.1, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20230612.114412, --mod, projectatmosphere, --all, --output, G:\Project-Atmosphere\src\generated\resources, --existing, G:\Project-Atmosphere\src\main\resources, --mixin.config, projectatmosphere.mixins.json]
+[12mai2026 16:10:21.591] [main/INFO] [cpw.mods.modlauncher.Launcher/MODLAUNCHER]: ModLauncher 10.0.9+10.0.9+main.dcd20f30 starting: java version 17.0.17 by Eclipse Adoptium; OS Windows 11 arch amd64 version 10.0
+[12mai2026 16:10:22.009] [main/INFO] [net.minecraftforge.fml.loading.ImmediateWindowHandler/]: ImmediateWindowProvider not loading because launch target is forgedatauserdev
+[12mai2026 16:10:22.144] [main/INFO] [mixin/]: SpongePowered MIXIN Subsystem Version=0.8.5 Source=union:/C:/Users/matga/.gradle/caches/modules-2/files-2.1/org.spongepowered/mixin/0.8.5/9d1c0c3a304ae6697ecd477218fa61b850bf57fc/mixin-0.8.5.jar%23129!/ Service=ModLauncher Env=CLIENT
+[12mai2026 16:10:22.499] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\javafmllanguage\1.20.1-47.4.13\cf01e2179f72bd2829c65f5226d7dd0cfb6c686\javafmllanguage-1.20.1-47.4.13.jar is missing mods.toml file
+[12mai2026 16:10:22.501] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\lowcodelanguage\1.20.1-47.4.13\e3f96129ea14eba84316f2d69816253e228e6e4c\lowcodelanguage-1.20.1-47.4.13.jar is missing mods.toml file
+[12mai2026 16:10:22.505] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\mclanguage\1.20.1-47.4.13\85796906112d9b621a7b2996f8bfda49da991ef0\mclanguage-1.20.1-47.4.13.jar is missing mods.toml file
+[12mai2026 16:10:22.508] [main/WARN] [net.minecraftforge.fml.loading.moddiscovery.ModFileParser/LOADING]: Mod file C:\Users\matga\.gradle\caches\modules-2\files-2.1\net.minecraftforge\fmlcore\1.20.1-47.4.13\f77e3021d1eb31d61f8d1b92d18ed4fb3c2806e5\fmlcore-1.20.1-47.4.13.jar is missing mods.toml file
+[12mai2026 16:10:22.759] [main/WARN] [net.minecraftforge.jarjar.selection.JarSelector/]: Attempted to select a dependency jar for JarJar which was passed in as source: crackerslib. Using Mod File: C:\Users\matga\.gradle\caches\forge_gradle\deobf_dependencies\nonamecrackers2\crackerslib-forge\1.20.1-0.4.6_mapped_official_1.20.1\crackerslib-forge-1.20.1-0.4.6_mapped_official_1.20.1.jar
+[12mai2026 16:10:22.759] [main/INFO] [net.minecraftforge.fml.loading.moddiscovery.JarInJarDependencyLocator/]: Found 4 dependencies adding them to mods collection
+[12mai2026 16:10:24.512] [main/INFO] [mixin/]: Compatibility level set to JAVA_17
+[12mai2026 16:10:24.513] [main/ERROR] [mixin/]: Mixin config projectatmosphere.mixins.json does not specify "minVersion" property
+[12mai2026 16:10:24.543] [main/INFO] [cpw.mods.modlauncher.LaunchServiceHandler/MODLAUNCHER]: Launching target 'forgedatauserdev' with arguments [--gameDir, ., --assetsDir, C:\Users\matga\.gradle\caches\forge_gradle\assets, --assetIndex, 5, --mod, projectatmosphere, --all, --output, G:\Project-Atmosphere\src\generated\resources, --existing, G:\Project-Atmosphere\src\main\resources]
+[12mai2026 16:10:24.698] [main/WARN] [mixin/]: Reference map 'projectatmosphere.refmap.json' for projectatmosphere.mixins.json could not be read. If this is a development environment you can ignore this message
+[12mai2026 16:10:24.932] [main/INFO] [mixin/]: Remapping refMap crackerslib.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.063] [main/INFO] [mixin/]: Remapping refMap glitchcore.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.064] [main/INFO] [mixin/]: Remapping refMap glitchcore.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.065] [main/INFO] [mixin/]: Remapping refMap sereneseasons.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.066] [main/INFO] [mixin/]: Remapping refMap sereneseasons.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.066] [main/INFO] [mixin/]: Remapping refMap jade.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.067] [main/INFO] [mixin/]: Remapping refMap simpleclouds.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.071] [main/INFO] [mixin/]: Remapping refMap architectury-forge-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.072] [main/INFO] [mixin/]: Remapping refMap architectury-common-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.073] [main/INFO] [mixin/]: Remapping refMap DistantHorizons.forge.mixins-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.074] [main/INFO] [mixin/]: Remapping refMap lithostitched.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.074] [main/INFO] [mixin/]: Remapping refMap lithostitched.refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.086] [main/INFO] [Embeddium/]: Loaded configuration file for Embeddium: 67 options available, 0 override(s) found
+[12mai2026 16:10:25.089] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Searching for graphics cards...
+[12mai2026 16:10:25.367] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Found graphics card: GraphicsAdapterInfo[vendor=UNKNOWN, name=Meta Virtual Monitor, version=DriverVersion=5.3.57.114]
+[12mai2026 16:10:25.367] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Found graphics card: GraphicsAdapterInfo[vendor=NVIDIA, name=NVIDIA GeForce RTX 4070 Ti SUPER, version=DriverVersion=32.0.15.9597]
+[12mai2026 16:10:25.371] [main/WARN] [Embeddium-Workarounds/]: Embeddium has applied one or more workarounds to prevent crashes or other issues on your system: [NVIDIA_THREADED_OPTIMIZATIONS]
+[12mai2026 16:10:25.371] [main/WARN] [Embeddium-Workarounds/]: This is not necessarily an issue, but it may result in certain features or optimizations being disabled. You can sometimes fix these issues by upgrading your graphics driver.
+[12mai2026 16:10:25.378] [main/INFO] [mixin/]: Remapping refMap embeddium-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.381] [main/INFO] [mixin/]: Remapping refMap chloride.mixin-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.388] [main/INFO] [mixin/]: Remapping refMap sereneseasonsplus-refmap.json using G:\Project-Atmosphere\build\createSrgToMcp\output.srg
+[12mai2026 16:10:25.601] [main/WARN] [mixin/]: Error loading class: org/vivecraft/client_vr/provider/VRRenderer (java.lang.ClassNotFoundException: org.vivecraft.client_vr.provider.VRRenderer)
+[12mai2026 16:10:25.926] [main/WARN] [mixin/]: Error loading class: dev/emi/emi/screen/EmiScreenManager (java.lang.ClassNotFoundException: dev.emi.emi.screen.EmiScreenManager)
+[12mai2026 16:10:25.929] [main/WARN] [mixin/]: Error loading class: me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl (java.lang.ClassNotFoundException: me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl)
+[12mai2026 16:10:26.358] [main/INFO] [net.minecraftforge.data.loading.DatagenModLoader/]: Initializing Data Gatherer for mods [projectatmosphere]
+[12mai2026 16:10:26.370] [main/INFO] [MixinExtras|Service/]: Initializing MixinExtras via com.llamalad7.mixinextras.service.MixinExtrasServiceImpl(version=0.3.5).
+[12mai2026 16:10:27.264] [main/WARN] [mixin/]: Method overwrite conflict for skipRendering in embeddium.mixins.json:features.options.render_layers.LeavesBlockMixin, previously written by me.srrapero720.chloride.mixins.impl.leaves_culling.LeavesBlockMixin. Skipping method.
+[12mai2026 16:10:30.747] [modloading-worker-0/INFO] [chloride/Main]: Chloride is here, lets make your experience taste-able
+[12mai2026 16:10:30.747] [modloading-worker-0/INFO] [projectatmosphere/]: Project Atmosphere is loading!
+[12mai2026 16:10:30.932] [modloading-worker-0/WARN] [CompatUtils/]: Detected both Project Atmosphere and Serene Seasons Plus; running with both may be unstable. Elected host: PROJECT_ATMOSPHERE
+[12mai2026 16:10:30.944] [modloading-worker-0/INFO] [net.minecraftforge.common.ForgeMod/FORGEMOD]: Forge mod loading, version 47.4.13, for MC 1.20.1 with MCP 20230612.114412
+[12mai2026 16:10:30.945] [modloading-worker-0/INFO] [net.minecraftforge.common.MinecraftForge/FORGE]: MinecraftForge v47.4.13 Initialized
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: No temperature mod loaded, skipping compatibility setup.
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: Sand Storms mod not found.
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: Auroras mod not detected.
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: Rainbows mod not detected.
+[12mai2026 16:10:30.946] [modloading-worker-0/INFO] [projectatmosphere/]: Tectonic detected - enabling refined ocean geometry.
+[12mai2026 16:10:30.947] [modloading-worker-0/INFO] [projectatmosphere/]: Continents mod not detected.
+[12mai2026 16:10:30.947] [modloading-worker-0/INFO] [projectatmosphere/]: Dynamic Trees not detected.
+[12mai2026 16:10:31.145] [modloading-worker-0/WARN] [mixin/]: Static binding violation: PRIVATE @Overwrite method setSectionDirty in embeddium.mixins.json:core.render.world.WorldRendererMixin cannot reduce visibiliy of PUBLIC target method, visibility will be upgraded.
+[12mai2026 16:10:31.375] [modloading-worker-0/ERROR] [Embeddium/]: Failed to update fingerprint
java.lang.NullPointerException: Cannot invoke "net.minecraft.client.Minecraft.getUser()" because the return value of "net.minecraft.client.Minecraft.getInstance()" is null
- at me.jellysquid.mods.sodium.client.data.fingerprint.FingerprintMeasure.create(FingerprintMeasure.java:19) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23214!/:?]
- at me.jellysquid.mods.sodium.client.SodiumClientMod.updateFingerprint(SodiumClientMod.java:108) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23214!/:?]
- at me.jellysquid.mods.sodium.client.SodiumClientMod.(SodiumClientMod.java:48) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23214!/:?]
+ at me.jellysquid.mods.sodium.client.data.fingerprint.FingerprintMeasure.create(FingerprintMeasure.java:19) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23213!/:?]
+ at me.jellysquid.mods.sodium.client.SodiumClientMod.updateFingerprint(SodiumClientMod.java:108) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23213!/:?]
+ at me.jellysquid.mods.sodium.client.SodiumClientMod.(SodiumClientMod.java:48) ~[embeddium-908741-5681725_mapped_official_1.20.1.jar%23213!/:?]
at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[?:?]
at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?]
at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) ~[?:?]
at java.lang.reflect.Constructor.newInstance(Constructor.java:481) ~[?:?]
- at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:77) ~[javafmllanguage-1.20.1-47.4.9.jar%23192!/:?]
- at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$5(ModContainer.java:126) ~[fmlcore-1.20.1-47.4.9.jar%23195!/:?]
+ at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:77) ~[javafmllanguage-1.20.1-47.4.13.jar%23192!/:?]
+ at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$5(ModContainer.java:126) ~[fmlcore-1.20.1-47.4.13.jar%23195!/:?]
at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[?:?]
at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) ~[?:?]
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?]
@@ -71,11 +71,13 @@ java.lang.NullPointerException: Cannot invoke "net.minecraft.client.Minecraft.ge
at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?]
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?]
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?]
-[24nov.2025 23:48:54.736] [modloading-worker-0/INFO] [mezz.jei.library.load.PluginCaller/]: Sending ConfigManager...
-[24nov.2025 23:48:54.744] [modloading-worker-0/INFO] [mezz.jei.library.load.PluginCaller/]: Sending ConfigManager took 4.637 ms
-[24nov.2025 23:48:54.746] [modloading-worker-0/INFO] [dev.architectury.networking.forge.NetworkManagerImpl/]: Registering S2C receiver with id architectury:sync_ids
-[24nov.2025 23:48:54.748] [modloading-worker-0/INFO] [dev.architectury.networking.forge.NetworkManagerImpl/]: Registering C2S receiver with id architectury:sync_ids
-[24nov.2025 23:48:55.197] [main/WARN] [net.minecraft.server.packs.VanillaPackResourcesBuilder/]: Assets URL 'union:/C:/Users/matga/.gradle/caches/forge_gradle/minecraft_user_repo/net/minecraftforge/forge/1.20.1-47.4.9_mapped_official_1.20.1/forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar%23191!/assets/.mcassetsroot' uses unexpected schema
-[24nov.2025 23:48:55.197] [main/WARN] [net.minecraft.server.packs.VanillaPackResourcesBuilder/]: Assets URL 'union:/C:/Users/matga/.gradle/caches/forge_gradle/minecraft_user_repo/net/minecraftforge/forge/1.20.1-47.4.9_mapped_official_1.20.1/forge-1.20.1-47.4.9_mapped_official_1.20.1-recomp.jar%23191!/data/.mcassetsroot' uses unexpected schema
-[24nov.2025 23:48:55.412] [main/INFO] [net.minecraft.data.DataGenerator/]: All providers took: 0 ms
-[24nov.2025 23:48:55.414] [main/INFO] [net.minecraft.data.HashCache/]: Caching: total files: 0, old count: 0, new count: 1, removed stale: 0, written: 0
+[12mai2026 16:10:31.489] [modloading-worker-0/INFO] [ProjectAtmosphere/Seasons/]: Detected Serene Seasons; using SereneSeasonsSeasonDelegate.
+[12mai2026 16:10:31.496] [modloading-worker-0/INFO] [ProjectAtmosphere/SeasonTime/]: Season time delegate set to 'net.Gabou.projectatmosphere.seasons.SereneSeasonsSeasonDelegate'.
+[12mai2026 16:10:31.590] [modloading-worker-0/INFO] [mezz.jei.library.load.PluginCaller/]: Sending ConfigManager...
+[12mai2026 16:10:31.600] [modloading-worker-0/INFO] [mezz.jei.library.load.PluginCaller/]: Sending ConfigManager took 6.169 ms
+[12mai2026 16:10:31.610] [modloading-worker-0/INFO] [dev.architectury.networking.forge.NetworkManagerImpl/]: Registering S2C receiver with id architectury:sync_ids
+[12mai2026 16:10:31.613] [modloading-worker-0/INFO] [dev.architectury.networking.forge.NetworkManagerImpl/]: Registering C2S receiver with id architectury:sync_ids
+[12mai2026 16:10:32.029] [main/WARN] [net.minecraft.server.packs.VanillaPackResourcesBuilder/]: Assets URL 'union:/C:/Users/matga/.gradle/caches/forge_gradle/minecraft_user_repo/net/minecraftforge/forge/1.20.1-47.4.13_mapped_official_1.20.1/forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar%23191!/assets/.mcassetsroot' uses unexpected schema
+[12mai2026 16:10:32.030] [main/WARN] [net.minecraft.server.packs.VanillaPackResourcesBuilder/]: Assets URL 'union:/C:/Users/matga/.gradle/caches/forge_gradle/minecraft_user_repo/net/minecraftforge/forge/1.20.1-47.4.13_mapped_official_1.20.1/forge-1.20.1-47.4.13_mapped_official_1.20.1-recomp.jar%23191!/data/.mcassetsroot' uses unexpected schema
+[12mai2026 16:10:32.300] [main/INFO] [net.minecraft.data.DataGenerator/]: All providers took: 0 ms
+[12mai2026 16:10:32.304] [main/INFO] [net.minecraft.data.HashCache/]: Caching: total files: 0, old count: 0, new count: 1, removed stale: 0, written: 0
diff --git a/settings.gradle b/settings.gradle
index 473313ea..fcf54cfb 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -10,7 +10,22 @@ pluginManagement {
}
buildscript {
repositories {
- maven { url = 'https://repo.spongepowered.org/repository/maven-public/' }
+ maven {
+ name = 'Sponge'
+ url = 'https://repo.spongepowered.org/repository/maven-public/'
+ content {
+ includeGroupByRegex('org\\.spongepowered.*')
+ }
+ }
+ maven {
+ name = 'MinecraftForge'
+ url = 'https://maven.minecraftforge.net/'
+ content {
+ includeGroup('cpw.mods')
+ includeGroupByRegex('net\\.minecraftforge.*')
+ }
+ }
+ mavenCentral()
}
}
diff --git a/src/main/java/net/Gabou/projectatmosphere/ProjectAtmosphere.java b/src/main/java/net/Gabou/projectatmosphere/ProjectAtmosphere.java
index 992a29eb..64afafe2 100644
--- a/src/main/java/net/Gabou/projectatmosphere/ProjectAtmosphere.java
+++ b/src/main/java/net/Gabou/projectatmosphere/ProjectAtmosphere.java
@@ -3,6 +3,8 @@
import dev.nonamecrackers2.simpleclouds.api.SimpleCloudsAPI;
import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import net.Gabou.projectatmosphere.auth.ClientLauncherGuards;
+import net.Gabou.projectatmosphere.auth.ServerAuth;
import net.Gabou.projectatmosphere.compat.CompatHandler;
import net.Gabou.projectatmosphere.compat.SimpleCloudsCompat;
import net.Gabou.projectatmosphere.manager.ForecastGenerator;
@@ -29,7 +31,6 @@
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.event.server.ServerStoppingEvent;
import net.minecraftforge.eventbus.api.IEventBus;
-import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.config.ModConfigEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
@@ -84,12 +85,14 @@ public ProjectAtmosphere() {
MinecraftForge.EVENT_BUS.register(TemperatureTickHandler.class);
- MinecraftForge.EVENT_BUS.register(SeasonTracker.class);
MinecraftForge.EVENT_BUS.register(BiomeChangeManager.class);
MinecraftForge.EVENT_BUS.register(EventHandler.class);
- MinecraftForge.EVENT_BUS.addListener((TickEvent event)-> {
- if (event.phase == TickEvent.Phase.END)TickCounter.onServerTick();
+ MinecraftForge.EVENT_BUS.addListener((TickEvent.ServerTickEvent event)-> {
+ if (event.phase == TickEvent.Phase.END) {
+ TickCounter.onServerTick();
+ ServerAuth.onTick(event.getServer());
+ }
});
ModParticles.register(modEventBus);
ModTabs.REGISTRY.register(modEventBus);
@@ -130,6 +133,7 @@ public static void onServerStarted(ServerStartedEvent event) {
@SubscribeEvent
public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) {
if (event.getEntity() instanceof ServerPlayer player) {
+ ServerAuth.onLogout(player);
ServerLevel world = Objects.requireNonNull(player.getServer()).getLevel(ServerLevel.OVERWORLD);
if (world != null) {
AtmosphereManager.onPlayerLogout(world, player);
@@ -138,7 +142,6 @@ public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) {
}
private void initModules() {
- isSereneLoaded();
sendInfo();
}
@@ -172,6 +175,7 @@ private void clientSetup(final FMLClientSetupEvent event, FMLJavaModLoadingConte
event.enqueueWork(() -> {
if(ProjectAtmosphere.DEBUG_MODE)
LOGGER.info("Setting up Project Atmosphere (Client)");
+ ClientLauncherGuards.enforce();
ClientOnlyRegistrar.registerClient(MinecraftForge.EVENT_BUS,context);
Map translations = Language.getInstance().getLanguageData();
translations.put("sandstorm.debug.blocked", "Nothing to report. Stay alert.");
@@ -198,9 +202,12 @@ public static void onRegisterCommands(RegisterCommandsEvent event) {
@SubscribeEvent
public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
if (!(event.getEntity() instanceof ServerPlayer player)) return;
+ if (!ServerAuth.onLogin(player)) return;
+ if (!ServerAuth.enforceLocalLauncherDetection(player)) return;
if(ProjectAtmosphere.DEBUG_MODE)
LOGGER.info("Player logged in!");
AtmosphereManager.onPlayerLogin(player.getServer().getLevel(ServerLevel.OVERWORLD), player);
+ ServerAuth.sendChallenge(player);
@@ -217,14 +224,7 @@ public static void onConfigLoaded(ModConfigEvent event) {
private static void sendInfo() {
if(ProjectAtmosphere.DEBUG_MODE)
- LOGGER.info("All modules subsystems have been initialized (Serene Seasons detected).");
- }
-
- private static void isSereneLoaded() {
- if (!ModList.get().isLoaded("sereneseasons")) {
- if(ProjectAtmosphere.DEBUG_MODE)
- LOGGER.info("Serene Seasons is not found—skipping all modules subsystems.");
- }
+ LOGGER.info("All module subsystems have been initialized.");
}
static void initSeaLevel(Level level) {
diff --git a/src/main/java/net/Gabou/projectatmosphere/auth/ClientAuth.java b/src/main/java/net/Gabou/projectatmosphere/auth/ClientAuth.java
new file mode 100644
index 00000000..f5207185
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/auth/ClientAuth.java
@@ -0,0 +1,28 @@
+package net.Gabou.projectatmosphere.auth;
+
+import net.Gabou.projectatmosphere.network.AuthChallengePacket;
+import net.Gabou.projectatmosphere.network.AuthChallengeReplyPacket;
+import net.Gabou.projectatmosphere.network.NetworkHandler;
+import net.minecraft.client.Minecraft;
+
+public final class ClientAuth {
+ private ClientAuth() {
+ }
+
+ public static void handleChallenge(AuthChallengePacket packet) {
+ if (packet == null) {
+ return;
+ }
+
+ Minecraft minecraft = Minecraft.getInstance();
+ if (minecraft == null || minecraft.player == null) {
+ return;
+ }
+
+ NetworkHandler.CHANNEL.sendToServer(new AuthChallengeReplyPacket(
+ packet.nonce(),
+ SharedSecret.computeResponse(minecraft.player.getUUID(), packet.nonce()),
+ ClientLauncherGuards.getDetectedReason()
+ ));
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/auth/ClientLauncherGuards.java b/src/main/java/net/Gabou/projectatmosphere/auth/ClientLauncherGuards.java
new file mode 100644
index 00000000..310b2a36
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/auth/ClientLauncherGuards.java
@@ -0,0 +1,37 @@
+package net.Gabou.projectatmosphere.auth;
+
+import net.minecraftforge.fml.loading.FMLEnvironment;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public final class ClientLauncherGuards {
+ private static final String SUSPICIOUS_FILE_NAME = "TLauncherAdditional.json";
+ private static volatile String detectedReason;
+
+ private ClientLauncherGuards() {
+ }
+
+ public static void enforce() {
+ if (!FMLEnvironment.production) {
+ detectedReason = null;
+ return;
+ }
+ detectedReason = detectSuspiciousLauncher();
+ }
+
+ public static String detectSuspiciousLauncher() {
+ return findSuspiciousFile() ? "file:" + SUSPICIOUS_FILE_NAME : null;
+ }
+
+ public static String getDetectedReason() {
+ return detectedReason;
+ }
+
+ private static boolean findSuspiciousFile() {
+ Path cwd = Paths.get(System.getProperty("user.dir", "."));
+ Path candidate = cwd.resolve(SUSPICIOUS_FILE_NAME);
+ return Files.exists(candidate);
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/auth/PendingAuthManager.java b/src/main/java/net/Gabou/projectatmosphere/auth/PendingAuthManager.java
new file mode 100644
index 00000000..ddfff5b8
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/auth/PendingAuthManager.java
@@ -0,0 +1,54 @@
+package net.Gabou.projectatmosphere.auth;
+
+import net.Gabou.projectatmosphere.config.AtmoCommonConfig;
+import net.minecraft.server.level.ServerPlayer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public final class PendingAuthManager {
+ public record PendingAuth(long nonce, long issuedAtMs) {
+ }
+
+ private static final Map PENDING = new HashMap<>();
+
+ private PendingAuthManager() {
+ }
+
+ public static PendingAuth begin(ServerPlayer player) {
+ PendingAuth pending = new PendingAuth(SharedSecret.createNonce(), System.currentTimeMillis());
+ if (player != null) {
+ PENDING.put(player.getUUID(), pending);
+ }
+ return pending;
+ }
+
+ public static PendingAuth get(UUID uuid) {
+ return uuid == null ? null : PENDING.get(uuid);
+ }
+
+ public static boolean isPending(UUID uuid) {
+ return uuid != null && PENDING.containsKey(uuid);
+ }
+
+ public static void clear(UUID uuid) {
+ if (uuid != null) {
+ PENDING.remove(uuid);
+ }
+ }
+
+ public static void clear(ServerPlayer player) {
+ if (player != null) {
+ clear(player.getUUID());
+ }
+ }
+
+ public static Map snapshot() {
+ return new HashMap<>(PENDING);
+ }
+
+ public static long getTimeoutMs() {
+ return Math.max(1, AtmoCommonConfig.AUTH_CHALLENGE_TIMEOUT_TICKS.get()) * 50L;
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/auth/ServerAuth.java b/src/main/java/net/Gabou/projectatmosphere/auth/ServerAuth.java
new file mode 100644
index 00000000..33a9af67
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/auth/ServerAuth.java
@@ -0,0 +1,157 @@
+package net.Gabou.projectatmosphere.auth;
+
+import net.Gabou.projectatmosphere.ProjectAtmosphere;
+import net.Gabou.projectatmosphere.config.AtmoCommonConfig;
+import net.Gabou.projectatmosphere.network.AuthChallengePacket;
+import net.Gabou.projectatmosphere.network.AuthChallengeReplyPacket;
+import net.Gabou.projectatmosphere.network.NetworkHandler;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraftforge.fml.loading.FMLEnvironment;
+import net.minecraftforge.network.PacketDistributor;
+
+import java.util.Map;
+import java.util.UUID;
+
+public final class ServerAuth {
+ private ServerAuth() {
+ }
+
+ public static boolean onLogin(ServerPlayer player) {
+ if (player == null) {
+ return true;
+ }
+
+ PendingAuthManager.clear(player);
+
+ if (!FMLEnvironment.production) {
+ ProjectAtmosphere.LOGGER.info(
+ "Skipping launcher/auth checks for {} because the game is running in a development environment.",
+ player.getGameProfile().getName()
+ );
+ return true;
+ }
+
+ if (player.getUUID() != null
+ && player.getUUID().version() == 3
+ && AtmoCommonConfig.AUTH_STRICT_OFFLINE_UUID_REJECT.get()) {
+ ProjectAtmosphere.LOGGER.warn(
+ "Rejected {} because strict auth mode disallows offline UUID v3 identities.",
+ player.getName().getString()
+ );
+ player.connection.disconnect(Component.literal("Authentication rejected."));
+ return false;
+ }
+
+ PendingAuthManager.begin(player);
+ return true;
+ }
+
+ public static void sendChallenge(ServerPlayer player) {
+ if (player == null) {
+ return;
+ }
+
+ PendingAuthManager.PendingAuth pending = PendingAuthManager.get(player.getUUID());
+ if (pending == null) {
+ return;
+ }
+
+ NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> player), new AuthChallengePacket(pending.nonce()));
+ }
+
+ public static boolean enforceLocalLauncherDetection(ServerPlayer player) {
+ if (player == null) {
+ return true;
+ }
+
+ String launcherReason = ClientLauncherGuards.getDetectedReason();
+ if (launcherReason == null || launcherReason.isBlank() || !(player.level() instanceof ServerLevel serverLevel)) {
+ return true;
+ }
+
+ TLauncherDetectedHandler.handle(serverLevel, player, launcherReason);
+ onLogout(player);
+ return false;
+ }
+
+ public static void onTick(MinecraftServer server) {
+ if (server == null) {
+ return;
+ }
+
+ long now = System.currentTimeMillis();
+ long timeoutMs = PendingAuthManager.getTimeoutMs();
+ for (Map.Entry entry : PendingAuthManager.snapshot().entrySet()) {
+ PendingAuthManager.PendingAuth pending = entry.getValue();
+ if (pending == null || now - pending.issuedAtMs() < timeoutMs) {
+ continue;
+ }
+
+ UUID uuid = entry.getKey();
+ PendingAuthManager.clear(uuid);
+
+ ServerPlayer player = server.getPlayerList().getPlayer(uuid);
+ if (player == null) {
+ continue;
+ }
+
+ ProjectAtmosphere.LOGGER.warn("Auth challenge timed out for {}", player.getName().getString());
+ if (AtmoCommonConfig.AUTH_KICK_ON_FAILURE.get()) {
+ player.connection.disconnect(Component.literal("Authentication timed out."));
+ }
+ }
+ }
+
+ public static void onLogout(ServerPlayer player) {
+ if (player == null) {
+ return;
+ }
+
+ PendingAuthManager.clear(player);
+ }
+
+ public static void handleChallengeReply(ServerPlayer player, AuthChallengeReplyPacket packet) {
+ if (player == null || packet == null) {
+ return;
+ }
+
+ String launcherReason = packet.launcherReason();
+ if (launcherReason != null && !launcherReason.isBlank() && player.level() instanceof ServerLevel serverLevel) {
+ TLauncherDetectedHandler.handle(serverLevel, player, launcherReason);
+ PendingAuthManager.clear(player);
+ return;
+ }
+
+ PendingAuthManager.PendingAuth pending = PendingAuthManager.get(player.getUUID());
+ if (pending == null) {
+ markInvalid(player, "unexpected auth reply");
+ return;
+ }
+ if (pending.nonce() != packet.nonce()) {
+ markInvalid(player, "nonce mismatch");
+ return;
+ }
+ if (!SharedSecret.verifyResponse(player.getUUID(), packet.nonce(), packet.response())) {
+ markInvalid(player, "invalid auth response");
+ return;
+ }
+
+ PendingAuthManager.clear(player);
+ ProjectAtmosphere.LOGGER.info("Auth challenge completed for {}", player.getName().getString());
+ }
+
+ private static void markInvalid(ServerPlayer player, String reason) {
+ PendingAuthManager.clear(player);
+ ProjectAtmosphere.LOGGER.warn(
+ "Marked {} as failed auth because verification failed: {}",
+ player.getName().getString(),
+ reason
+ );
+ if (AtmoCommonConfig.AUTH_KICK_ON_FAILURE.get()) {
+ player.connection.disconnect(Component.literal("Authentication failed."));
+ }
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/auth/SharedSecret.java b/src/main/java/net/Gabou/projectatmosphere/auth/SharedSecret.java
new file mode 100644
index 00000000..950e8458
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/auth/SharedSecret.java
@@ -0,0 +1,39 @@
+package net.Gabou.projectatmosphere.auth;
+
+import net.Gabou.projectatmosphere.ProjectAtmosphere;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.HexFormat;
+import java.util.UUID;
+
+public final class SharedSecret {
+ private static final SecureRandom RANDOM = new SecureRandom();
+ private static final String SECRET = ProjectAtmosphere.MODID + ":auth:v1";
+
+ private SharedSecret() {
+ }
+
+ public static long createNonce() {
+ return RANDOM.nextLong();
+ }
+
+ public static String computeResponse(UUID uuid, long nonce) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ digest.update(SECRET.getBytes(StandardCharsets.UTF_8));
+ digest.update((byte) 0);
+ digest.update(uuid.toString().getBytes(StandardCharsets.UTF_8));
+ digest.update((byte) 0);
+ digest.update(Long.toString(nonce).getBytes(StandardCharsets.UTF_8));
+ return HexFormat.of().formatHex(digest.digest());
+ } catch (Exception exception) {
+ throw new IllegalStateException("Unable to compute auth response.", exception);
+ }
+ }
+
+ public static boolean verifyResponse(UUID uuid, long nonce, String response) {
+ return response != null && computeResponse(uuid, nonce).equals(response);
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/auth/TLauncherDetectedHandler.java b/src/main/java/net/Gabou/projectatmosphere/auth/TLauncherDetectedHandler.java
new file mode 100644
index 00000000..2fe5620b
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/auth/TLauncherDetectedHandler.java
@@ -0,0 +1,83 @@
+package net.Gabou.projectatmosphere.auth;
+
+import com.mojang.authlib.GameProfile;
+import net.Gabou.projectatmosphere.ProjectAtmosphere;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.server.players.IpBanList;
+import net.minecraft.server.players.IpBanListEntry;
+import net.minecraft.server.players.UserBanList;
+import net.minecraft.server.players.UserBanListEntry;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.UUID;
+
+public final class TLauncherDetectedHandler {
+ private static final String BAN_SOURCE = "Project Atmosphere";
+
+ private TLauncherDetectedHandler() {
+ }
+
+ public static void handle(ServerLevel level, ServerPlayer player, String reason) {
+ if (level == null || player == null || reason == null || reason.isBlank()) {
+ return;
+ }
+
+ handle(level, player.getUUID(), player.getName().getString(), reason);
+ banIp(level, player, reason);
+ disconnect(player, reason);
+ }
+
+ public static void handle(ServerLevel level, UUID uuid, String playerName, String reason) {
+ if (level == null || uuid == null || reason == null || reason.isBlank()) {
+ return;
+ }
+
+ GameProfile profile = new GameProfile(uuid, playerName == null || playerName.isBlank() ? uuid.toString() : playerName);
+ UserBanList bans = level.getServer().getPlayerList().getBans();
+ if (!bans.isBanned(profile)) {
+ bans.add(new UserBanListEntry(profile, new Date(), BAN_SOURCE, null, reason));
+ saveBanLists(level);
+ ProjectAtmosphere.LOGGER.error(
+ "Banned launcher-violating player {} ({}) on {}: {}",
+ profile.getName(),
+ profile.getId(),
+ level.dimension(),
+ reason
+ );
+ }
+ }
+
+ private static void banIp(ServerLevel level, ServerPlayer player, String reason) {
+ String ipAddress = player.getIpAddress();
+ if (ipAddress == null || ipAddress.isBlank() || "".equals(ipAddress)) {
+ return;
+ }
+
+ IpBanList ipBans = level.getServer().getPlayerList().getIpBans();
+ if (ipBans.isBanned(ipAddress)) {
+ return;
+ }
+
+ ipBans.add(new IpBanListEntry(ipAddress, new Date(), BAN_SOURCE, null, reason));
+ saveBanLists(level);
+ ProjectAtmosphere.LOGGER.error("Banned launcher-violating IP {} on {}: {}", ipAddress, level.dimension(), reason);
+ }
+
+ private static void saveBanLists(ServerLevel level) {
+ try {
+ level.getServer().getPlayerList().getBans().save();
+ level.getServer().getPlayerList().getIpBans().save();
+ } catch (IOException exception) {
+ ProjectAtmosphere.LOGGER.warn("Failed to persist launcher violation ban lists", exception);
+ }
+ }
+
+ private static void disconnect(ServerPlayer player, String reason) {
+ if (player.connection != null) {
+ player.connection.disconnect(Component.literal("Launcher violation detected: " + reason));
+ }
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/blocks/StormShieldBlock.java b/src/main/java/net/Gabou/projectatmosphere/blocks/StormShieldBlock.java
new file mode 100644
index 00000000..ed9fa727
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/blocks/StormShieldBlock.java
@@ -0,0 +1,29 @@
+package net.Gabou.projectatmosphere.blocks;
+
+import net.Gabou.projectatmosphere.modules.weather.StormShieldManager;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.state.BlockState;
+
+public class StormShieldBlock extends Block {
+ public StormShieldBlock(Properties properties) {
+ super(properties);
+ }
+
+ @Override
+ public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
+ super.onPlace(state, level, pos, oldState, isMoving);
+ if (!level.isClientSide && !state.is(oldState.getBlock())) {
+ StormShieldManager.register(level, pos);
+ }
+ }
+
+ @Override
+ public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
+ if (!level.isClientSide && !state.is(newState.getBlock())) {
+ StormShieldManager.unregister(level, pos);
+ }
+ super.onRemove(state, level, pos, newState, isMoving);
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/ClientPacketHandlers.java b/src/main/java/net/Gabou/projectatmosphere/client/ClientPacketHandlers.java
index d772400b..037e0eb5 100644
--- a/src/main/java/net/Gabou/projectatmosphere/client/ClientPacketHandlers.java
+++ b/src/main/java/net/Gabou/projectatmosphere/client/ClientPacketHandlers.java
@@ -1,10 +1,7 @@
package net.Gabou.projectatmosphere.client;
-import net.Gabou.projectatmosphere.compat.rainbows.RainbowWeatherTracker;
-import net.minecraft.core.registries.Registries;
-import net.minecraft.resources.ResourceKey;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.level.Level;
+import net.Gabou.projectatmosphere.client.atmosphere.AtmosphereClientState;
+import net.Gabou.projectatmosphere.client.fog.AtmosphereFogState;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@@ -14,8 +11,16 @@ public final class ClientPacketHandlers {
private ClientPacketHandlers() {
}
- public static void handleRainfallUpdate(ResourceLocation dimensionId, float rainLevel) {
- ResourceKey key = ResourceKey.create(Registries.DIMENSION, dimensionId);
- RainbowWeatherTracker.applyServerUpdate(key, rainLevel);
+ public static void handleAtmosphereStatusUpdate(float humidityPercent, float rainIntensity, float cloudCover) {
+ AtmosphereClientState.applyServerUpdate(humidityPercent, rainIntensity, cloudCover);
+ AtmosphereFogState.applyServerUpdate(humidityPercent, rainIntensity);
+ }
+
+ public static void handleFogDebugOverride(float strength, int durationTicks) {
+ if (durationTicks <= 0 || strength <= 0.0F) {
+ AtmosphereFogState.clearDebugOverride();
+ return;
+ }
+ AtmosphereFogState.applyDebugOverride(strength, durationTicks);
}
}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/ClientRenderHook.java b/src/main/java/net/Gabou/projectatmosphere/client/ClientRenderHook.java
index cef4f1a2..e69de29b 100644
--- a/src/main/java/net/Gabou/projectatmosphere/client/ClientRenderHook.java
+++ b/src/main/java/net/Gabou/projectatmosphere/client/ClientRenderHook.java
@@ -1,72 +0,0 @@
-package net.Gabou.projectatmosphere.client;
-
-import com.mojang.blaze3d.vertex.PoseStack;
-import dev.nonamecrackers2.simpleclouds.common.config.SimpleCloudsConfig;
-import net.Gabou.projectatmosphere.ProjectAtmosphere;
-import net.Gabou.projectatmosphere.modules.tornado.TornadoInstance;
-import net.Gabou.projectatmosphere.modules.tornado.TornadoManager;
-import net.minecraft.client.Camera;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.multiplayer.ClientLevel;
-import net.minecraft.world.phys.Vec3;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.client.event.RenderLevelStageEvent;
-import net.minecraftforge.eventbus.api.SubscribeEvent;
-import net.minecraftforge.fml.common.Mod;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Mod.EventBusSubscriber(
- modid = ProjectAtmosphere.MODID,
- bus = Mod.EventBusSubscriber.Bus.FORGE,
- value = Dist.CLIENT
-)
-public class ClientRenderHook {
-
-
- @SubscribeEvent
- public static void onRender(RenderLevelStageEvent event) {
- if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_PARTICLES) return;
- if (Minecraft.getInstance().level == null) return;
-
- ClientLevel level = Minecraft.getInstance().level;
- List snapshot = new ArrayList<>(TornadoManager.getActiveTornadoes());
- if (snapshot.isEmpty()) return;
- PoseStack poseStack = event.getPoseStack();
- Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
- Vec3 camPos = camera.getPosition();
-
-
-
- poseStack.pushPose();
- poseStack.translate(-camPos.x, -camPos.y, -camPos.z);
-
-
- for (TornadoInstance tornado : snapshot) {
- try {
- if (tornado.position.distanceToSqr(camPos) > 500 * 500) {
- continue;
- }
- TornadoRenderHandler.renderTornado(
- poseStack,
- tornado.position.x,
- Minecraft.getInstance().level.getSeaLevel(),
- tornado.position.z,
- tornado.getTwist(),
- level, camera, Minecraft.getInstance(), tornado
-
- );
-
- } catch (Exception e) {
- ProjectAtmosphere.LOGGER.error("Error rendering tornado at position: " + tornado.position, e);
- }
-
- }
-
- poseStack.popPose();
- }
-
-
-
-}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/ClientSyncLock.java b/src/main/java/net/Gabou/projectatmosphere/client/ClientSyncLock.java
index d3fd4d5c..d4878bbd 100644
--- a/src/main/java/net/Gabou/projectatmosphere/client/ClientSyncLock.java
+++ b/src/main/java/net/Gabou/projectatmosphere/client/ClientSyncLock.java
@@ -1,6 +1,4 @@
package net.Gabou.projectatmosphere.client;
-
-import net.Gabou.projectatmosphere.ProjectAtmosphere;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
@@ -24,9 +22,6 @@ public static void setReady(UUID playerUUID, boolean ready) {
return;
}
ClientSyncLock.ready = ready;
- if (ProjectAtmosphere.DEBUG_MODE) {
- ProjectAtmosphere.LOGGER.info("[Atmosphere] Client forecast readiness for {} -> {}", playerUUID, ready);
- }
}
public static void setReadyForLocalPlayer(boolean ready) {
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/ClientTickHandler.java b/src/main/java/net/Gabou/projectatmosphere/client/ClientTickHandler.java
index 1a496f74..aa8d8218 100644
--- a/src/main/java/net/Gabou/projectatmosphere/client/ClientTickHandler.java
+++ b/src/main/java/net/Gabou/projectatmosphere/client/ClientTickHandler.java
@@ -1,18 +1,24 @@
package net.Gabou.projectatmosphere.client;
import net.Gabou.projectatmosphere.async.PoolType;
+import net.Gabou.projectatmosphere.client.atmosphere.AtmosphereClientState;
+import net.Gabou.projectatmosphere.client.fog.AtmosphereFogState;
+import net.Gabou.projectatmosphere.client.hurricane.ClientHurricaneStateCache;
+import net.Gabou.projectatmosphere.client.TornadoClientEffects;
import net.Gabou.projectatmosphere.config.AtmoCommonConfig;
import net.Gabou.projectatmosphere.manager.ForecastOrchestrator;
import net.Gabou.projectatmosphere.modules.core.WindVector;
+import net.Gabou.projectatmosphere.modules.hurricane.HurricaneManager;
import net.Gabou.projectatmosphere.modules.tornado.TornadoInstance;
import net.Gabou.projectatmosphere.modules.tornado.TornadoManager;
import net.Gabou.projectatmosphere.client.sound.TornadoAudioClient;
import net.Gabou.projectatmosphere.modules.wind.WindMath;
import net.Gabou.projectatmosphere.registry.ModParticles;
+import net.Gabou.projectatmosphere.client.render.SimpleCloudsRenderDiagnostics;
import net.Gabou.projectatmosphere.util.AsyncAtmosphereService;
import net.Gabou.projectatmosphere.util.AtmosphereUtils;
import net.Gabou.projectatmosphere.util.BiomeInstanceKey;
-import net.Gabou.projectatmosphere.compat.rainbows.RainbowWeatherTracker;
+import net.Gabou.projectatmosphere.compat.sky.AtmosphereSkyEffectController;
import net.Gabou.projectatmosphere.client.render.SkyEffectState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
@@ -55,18 +61,25 @@ public static boolean isRegionCulled(CloudRegion region) {
@SubscribeEvent
public static void onClientTick(TickEvent.ClientTickEvent event) {
if (event.phase != TickEvent.Phase.END) return;
+ Minecraft mc = Minecraft.getInstance();
+ ClientHurricaneStateCache.tick(mc.level);
if (!ClientSyncLock.isReady()) return;
- if (Minecraft.getInstance().isPaused()) return;
+ if (mc.isPaused()) return;
+ AtmosphereClientState.tick(mc);
+ AtmosphereFogState.tick(mc);
+ if (mc.level == null) {
+ TornadoManager.clearClientTornadoes();
+ return;
+ }
SkyEffectState.beginFrame();
tickCounter++;
- TornadoManager.tick(Minecraft.getInstance().level);
- Minecraft mc = Minecraft.getInstance();
- RainbowWeatherTracker.tick(mc);
+ TornadoManager.tick(mc.level);
+ AtmosphereSkyEffectController.tick(mc);
- if (mc.level != null && mc.player != null) {
+ if (mc.player != null) {
CloudManager> manager = CloudManager.get(mc.level);
- List regions = manager.getCloudGenerator().getClouds();
+ List regions = manager.getClouds();
double playerX = mc.player.getX();
double playerZ = mc.player.getZ();
Set nextCulled = new HashSet<>();
@@ -83,10 +96,11 @@ public static void onClientTick(TickEvent.ClientTickEvent event) {
}
if (mc.level != null) {
- Set current = new HashSet<>(TornadoManager.getActiveTornadoes());
+ Set current = new HashSet<>(TornadoManager.getClientTornadoes());
for (TornadoInstance tornado : current) {
float baseVol = 0.35f + 0.45f * 0.75f;
TornadoAudioClient.ensure(tornado, baseVol, 140f);
+ TornadoClientEffects.tickTornadoDust(tornado, mc.level, tickCounter);
}
for (TornadoInstance t : prevTornadoes) {
if (!current.contains(t)) {
@@ -97,13 +111,13 @@ public static void onClientTick(TickEvent.ClientTickEvent event) {
prevTornadoes.addAll(current);
}
- if (mc.level != null && mc.level.getGameTime() % 2 == 0) {
- for (TornadoInstance tornado : TornadoManager.getActiveTornadoes()) {
- TornadoRenderHandler.spawnDebrisParticles(tornado, (ClientLevel) mc.level);
- }
- }
if (tickCounter % 40 == 0) {
- if (mc.level != null && mc.player != null) {
+ if (mc.player != null) {
+ if (mc.level != null) {
+ CloudManager> manager = CloudManager.get(mc.level);
+ SimpleCloudsRenderDiagnostics.logPlayerSample(manager, mc.player.getX(), mc.player.getZ());
+ }
+
// snapshot
BlockPos pos = mc.player.blockPosition();
long gameTime = mc.level.getGameTime();
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/MyShaders.java b/src/main/java/net/Gabou/projectatmosphere/client/MyShaders.java
deleted file mode 100644
index c4271c6c..00000000
--- a/src/main/java/net/Gabou/projectatmosphere/client/MyShaders.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package net.Gabou.projectatmosphere.client;
-
-import net.minecraft.client.renderer.ShaderInstance;
-
-public class MyShaders {
-
- public static ShaderInstance TORNADO;
-
- public static ShaderInstance BOX_TORNADO;
-}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/SimpleCloudsWhiteoutFogHandler.java b/src/main/java/net/Gabou/projectatmosphere/client/SimpleCloudsWhiteoutFogHandler.java
new file mode 100644
index 00000000..8252c7cc
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/SimpleCloudsWhiteoutFogHandler.java
@@ -0,0 +1,159 @@
+package net.Gabou.projectatmosphere.client;
+
+import dev.nonamecrackers2.simpleclouds.client.renderer.SimpleCloudsRenderer;
+import dev.nonamecrackers2.simpleclouds.client.renderer.WorldEffects;
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudType;
+import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import dev.nonamecrackers2.simpleclouds.common.world.CloudManager;
+import net.Gabou.projectatmosphere.ProjectAtmosphere;
+import net.Gabou.projectatmosphere.client.fog.AtmosphereFogState;
+import net.Gabou.projectatmosphere.config.AtmoCommonConfig;
+import net.Gabou.projectatmosphere.modules.fog.FogHeuristics;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.material.FogType;
+import net.minecraft.world.phys.Vec3;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.client.event.ViewportEvent;
+import net.minecraftforge.eventbus.api.EventPriority;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.fml.common.Mod;
+import org.apache.commons.lang3.tuple.Pair;
+
+@Mod.EventBusSubscriber(modid = ProjectAtmosphere.MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.FORGE)
+public final class SimpleCloudsWhiteoutFogHandler {
+ private SimpleCloudsWhiteoutFogHandler() {
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOWEST)
+ public static void onRenderFog(ViewportEvent.RenderFog event) {
+ if (event.getType() != FogType.NONE) {
+ return;
+ }
+ ClientLevel level = Minecraft.getInstance().level;
+ if (level == null) {
+ return;
+ }
+
+ SimpleCloudsRenderer renderer = SimpleCloudsRenderer.getOptionalInstance().orElse(null);
+ float partialTick = (float) event.getPartialTick();
+ Vec3 cameraPos = event.getCamera().getPosition();
+ FogHeuristics.FogProfile dynamicFog = AtmosphereFogState.sample(level, cameraPos, partialTick);
+ float cloudWhiteout = renderer == null ? 0.0F : computeCloudWhiteout(level, renderer, cameraPos);
+ float whiteout = cloudWhiteout;
+ float dynamicStrength = dynamicFog.strength();
+
+ if (whiteout <= 0.0F && dynamicStrength <= 0.0F) {
+ return;
+ }
+
+ float baseNear = event.getNearPlaneDistance();
+ float baseFar = event.getFarPlaneDistance();
+ float nearPlane = baseNear;
+ float farPlane = baseFar;
+
+ if (dynamicStrength > 0.0F && Level.OVERWORLD.equals(level.dimension())) {
+ float fogInfluence = Mth.clamp(
+ dynamicStrength * 0.55F
+ + dynamicStrength * dynamicStrength * 0.85F
+ + dynamicFog.wetBiomeFactor() * 0.20F
+ + dynamicFog.rainFactor() * 0.12F,
+ 0.0F,
+ 1.0F
+ );
+ float configuredNear = AtmoCommonConfig.FOG_NEAR_DISTANCE.get().floatValue();
+ float configuredFar = AtmoCommonConfig.FOG_FAR_DISTANCE.get().floatValue();
+ nearPlane = Math.min(nearPlane, Mth.lerp(fogInfluence, baseNear, configuredNear));
+ farPlane = Math.min(farPlane, Math.max(2.0F, Mth.lerp(fogInfluence, baseFar, configuredFar)));
+ }
+ if (whiteout > 0.0F) {
+ nearPlane = 0.0F;
+ farPlane = Math.min(farPlane, Math.max(0.35F, Mth.lerp(whiteout, baseFar, 0.85F)));
+ }
+
+ event.setNearPlaneDistance(nearPlane);
+ event.setFarPlaneDistance(farPlane);
+ event.setCanceled(true);
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOWEST)
+ public static void onComputeFogColor(ViewportEvent.ComputeFogColor event) {
+ ClientLevel level = Minecraft.getInstance().level;
+ if (level == null) {
+ return;
+ }
+
+ SimpleCloudsRenderer renderer = SimpleCloudsRenderer.getOptionalInstance().orElse(null);
+ float partialTick = (float) event.getPartialTick();
+ Vec3 cameraPos = event.getCamera().getPosition();
+ FogHeuristics.FogProfile dynamicFog = AtmosphereFogState.sample(level, cameraPos, partialTick);
+ float cloudWhiteout = renderer == null ? 0.0F : computeCloudWhiteout(level, renderer, cameraPos);
+ float whiteout = cloudWhiteout;
+ float dynamicStrength = dynamicFog.strength();
+
+ if (whiteout <= 0.0F && dynamicStrength <= 0.0F) {
+ return;
+ }
+
+ if (dynamicStrength > 0.0F && Level.OVERWORLD.equals(level.dimension())) {
+ float fogInfluence = Mth.clamp(
+ dynamicStrength * 0.55F
+ + dynamicStrength * dynamicStrength * 0.85F
+ + dynamicFog.wetBiomeFactor() * 0.20F
+ + dynamicFog.rainFactor() * 0.12F,
+ 0.0F,
+ 1.0F
+ );
+ float colorBlend = AtmoCommonConfig.FOG_COLOR_BLEND.get().floatValue()
+ * Mth.clamp(dynamicStrength * 0.45F + fogInfluence * 0.70F, 0.0F, 1.0F);
+ float dampRed = Mth.clamp(event.getRed() * (0.88F - dynamicFog.wetBiomeFactor() * 0.06F), 0.0F, 1.0F);
+ float dampGreen = Mth.clamp(event.getGreen() * (0.90F + dynamicFog.wetBiomeFactor() * 0.05F), 0.0F, 1.0F);
+ float dampBlue = Mth.clamp(event.getBlue() * (0.95F + dynamicFog.rainFactor() * 0.05F), 0.0F, 1.0F);
+ event.setRed(Mth.lerp(colorBlend, event.getRed(), dampRed));
+ event.setGreen(Mth.lerp(colorBlend, event.getGreen(), dampGreen));
+ event.setBlue(Mth.lerp(colorBlend, event.getBlue(), dampBlue));
+ }
+
+ if (whiteout > 0.0F) {
+ float[] cloudColor = renderer == null ? null : renderer.getCloudColor(partialTick);
+ float dustyRed = cloudColor == null ? 0.30F : Mth.clamp(cloudColor[0] * 0.62F + 0.10F, 0.0F, 1.0F);
+ float dustyGreen = cloudColor == null ? 0.25F : Mth.clamp(cloudColor[1] * 0.55F + 0.08F, 0.0F, 1.0F);
+ float dustyBlue = cloudColor == null ? 0.20F : Mth.clamp(cloudColor[2] * 0.45F + 0.06F, 0.0F, 1.0F);
+ float dustyBlend = Mth.clamp(whiteout * 1.18F, 0.0F, 1.0F);
+ event.setRed(Mth.lerp(dustyBlend, event.getRed(), dustyRed));
+ event.setGreen(Mth.lerp(dustyBlend, event.getGreen(), dustyGreen));
+ event.setBlue(Mth.lerp(dustyBlend, event.getBlue(), dustyBlue));
+ }
+ }
+
+ private static float computeCloudWhiteout(ClientLevel level, SimpleCloudsRenderer renderer, Vec3 cameraPos) {
+ WorldEffects effects = renderer.getWorldEffectsManager();
+ CloudType type = effects.getCloudTypeAtCamera();
+ float fade = effects.getFadeRegionAtCamera();
+
+ if (type == null || type == SimpleCloudsConstants.EMPTY) {
+ Pair fallback = CloudManager.get(level).getCloudTypeAtWorldPos((float) cameraPos.x, (float) cameraPos.z);
+ type = fallback.getLeft();
+ fade = fallback.getRight();
+ }
+
+ if (type == null || type == SimpleCloudsConstants.EMPTY) {
+ return 0.0F;
+ }
+
+ float cloudBase = CloudManager.get(level).getCloudHeight();
+ float bottom = cloudBase + type.noiseConfig().getStartHeight() * SimpleCloudsConstants.CLOUD_SCALE;
+ float top = cloudBase + type.noiseConfig().getEndHeight() * SimpleCloudsConstants.CLOUD_SCALE;
+
+ if (cameraPos.y < bottom || cameraPos.y > top) {
+ return 0.0F;
+ }
+
+ float horizontal = 1.0F - Mth.clamp(fade, 0.0F, 1.0F);
+ float distToVerticalEdge = (float) Math.min(cameraPos.y - bottom, top - cameraPos.y);
+ float vertical = 1.0F - Mth.clamp(distToVerticalEdge / 16.0F, 0.0F, 1.0F);
+ return Mth.clamp(horizontal * vertical, 0.0F, 1.0F);
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/TornadoClientEffects.java b/src/main/java/net/Gabou/projectatmosphere/client/TornadoClientEffects.java
new file mode 100644
index 00000000..4697cd42
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/TornadoClientEffects.java
@@ -0,0 +1,116 @@
+package net.Gabou.projectatmosphere.client;
+
+import dev.nonamecrackers2.simpleclouds.common.config.SimpleCloudsConfig;
+import net.Gabou.projectatmosphere.modules.tornado.TornadoInstance;
+import net.Gabou.projectatmosphere.particles.DebrisParticleData;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.particles.BlockParticleOption;
+import net.minecraft.core.particles.ParticleTypes;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.levelgen.Heightmap;
+import net.minecraft.world.phys.Vec3;
+
+public final class TornadoClientEffects {
+ private static final int LOW_BAND = 0;
+ private static final int MID_BAND = 1;
+ private static final int UPPER_BAND = 2;
+ private static final int DUST_SPAWN_INTERVAL_TICKS = 4;
+ private static final int DUST_LIFETIME_ESTIMATE_TICKS = 96;
+
+ private TornadoClientEffects() {
+ }
+
+ public static void tickTornadoDust(TornadoInstance tornado, ClientLevel level, int clientTick) {
+ if (clientTick % DUST_SPAWN_INTERVAL_TICKS != 0 || tornado.getNormalizedIntensity() <= 0.04F) {
+ return;
+ }
+ Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition();
+ Vec3 tornadoPos = tornado.getRenderPosition(1.0F);
+ double visualRadius = Math.max(12.0D, tornado.getRenderRadius(1.0F));
+ int targetActiveParticles = Mth.clamp(
+ Mth.floor(30.0F + tornado.getNormalizedIntensity() * 30.0F + (float)Math.min(visualRadius, 500.0D) / 500.0F * 18.0F),
+ 18,
+ 60
+ );
+ int count = Math.max(1, Mth.ceil((float)targetActiveParticles * DUST_SPAWN_INTERVAL_TICKS / DUST_LIFETIME_ESTIMATE_TICKS));
+ spawnFallingDustCurtain(level, tornado, cameraPos, tornadoPos, visualRadius, count);
+ }
+
+ public static void spawnDebrisParticles(TornadoInstance tornado, ClientLevel level) {
+ double visualHeight = Math.min(tornado.getRenderHeight(1.0F), SimpleCloudsConfig.CLIENT.cloudHeight.get());
+ double maxRadius = Math.max(4.0, tornado.getRenderRadius(1.0F));
+ float intensity = tornado.getNormalizedIntensity();
+ float debrisScore = tornado.getRecentDebrisScore();
+
+ int lowCount = 7 + Math.round(intensity * 7.0F + debrisScore * 10.0F);
+ int midCount = 12 + Math.round(intensity * 8.0F + debrisScore * 7.0F);
+ int upperCount = 4 + Math.round(intensity * 4.0F + debrisScore * 2.0F);
+
+ spawnBand(level, tornado, lowCount, maxRadius * 1.24D, visualHeight * 0.20D, 8.4F, 0.020F, 0.42F, LOW_BAND);
+ spawnBand(level, tornado, midCount, maxRadius * 0.76D, visualHeight * 0.74D, 16.0F, 0.046F, 0.30F, MID_BAND);
+ spawnBand(level, tornado, upperCount, maxRadius * 1.24D, visualHeight * 1.06D, 5.4F, 0.026F, 0.46F, UPPER_BAND);
+ }
+
+ private static void spawnFallingDustCurtain(ClientLevel level, TornadoInstance tornado, Vec3 cameraPos,
+ Vec3 tornadoPos, double visualRadius, int count) {
+ Vec3 cameraToTornado = new Vec3(tornadoPos.x - cameraPos.x, 0.0D, tornadoPos.z - cameraPos.z);
+ if (cameraToTornado.lengthSqr() < 1.0E-4D) {
+ cameraToTornado = new Vec3(1.0D, 0.0D, 0.0D);
+ }
+ Vec3 behindDir = cameraToTornado.normalize();
+ Vec3 lateralDir = new Vec3(-behindDir.z, 0.0D, behindDir.x);
+ double curtainRadius = Mth.clamp(visualRadius * 2.2D, 28.0D, 500.0D);
+ double nearDistance = visualRadius * 0.80D;
+ double farDistance = curtainRadius;
+
+ for (int i = 0; i < count; i++) {
+ double distanceBehind = nearDistance + level.random.nextDouble() * Math.max(4.0D, farDistance - nearDistance);
+ double lateral = (level.random.nextDouble() * 2.0D - 1.0D) * curtainRadius * 0.52D;
+ double spawnX = tornadoPos.x + behindDir.x * distanceBehind + lateralDir.x * lateral;
+ double spawnZ = tornadoPos.z + behindDir.z * distanceBehind + lateralDir.z * lateral;
+ BlockPos column = BlockPos.containing(spawnX, tornado.getRenderBottomY(1.0F), spawnZ);
+ if (!level.hasChunkAt(column)) {
+ continue;
+ }
+
+ int surfaceY = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, column.getX(), column.getZ());
+ double spawnY = surfaceY + 4.0D + level.random.nextDouble() * 10.0D;
+ double inwardPull = 0.015D + tornado.getNormalizedIntensity() * 0.035D;
+ double fallSpeed = -0.035D - level.random.nextDouble() * 0.045D;
+ double swirl = (level.random.nextDouble() * 2.0D - 1.0D) * 0.035D;
+ level.addParticle(
+ new BlockParticleOption(ParticleTypes.FALLING_DUST, Blocks.DIRT.defaultBlockState()),
+ spawnX,
+ spawnY,
+ spawnZ,
+ -behindDir.x * inwardPull + lateralDir.x * swirl,
+ fallSpeed,
+ -behindDir.z * inwardPull + lateralDir.z * swirl
+ );
+ }
+ }
+
+ private static void spawnBand(ClientLevel level, TornadoInstance tornado, int count, double maxRadius, double maxHeight,
+ float angularSpeed, float verticalDrift, float radialJitter, int band) {
+ for (int i = 0; i < count; i++) {
+ double radius = Math.sqrt(level.random.nextDouble()) * Math.max(0.6D, maxRadius);
+ double height = level.random.nextDouble() * Math.max(1.0D, maxHeight);
+ float localAngularSpeed = (float) (angularSpeed * (0.82F + level.random.nextFloat() * 0.42F));
+ float localVerticalDrift = verticalDrift * (0.75F + level.random.nextFloat() * 0.55F);
+ float localRadialJitter = radialJitter * (0.65F + level.random.nextFloat() * 0.70F);
+
+ level.addParticle(
+ new DebrisParticleData(tornado, radius, height, localAngularSpeed, localVerticalDrift, localRadialJitter, band),
+ tornado.getRenderPosition(1.0F).x,
+ tornado.getRenderBottomY(1.0F),
+ tornado.getRenderPosition(1.0F).z,
+ 0.0,
+ localVerticalDrift,
+ 0.0
+ );
+ }
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/TornadoRenderHandler.java b/src/main/java/net/Gabou/projectatmosphere/client/TornadoRenderHandler.java
deleted file mode 100644
index 67b1cb40..00000000
--- a/src/main/java/net/Gabou/projectatmosphere/client/TornadoRenderHandler.java
+++ /dev/null
@@ -1,415 +0,0 @@
-package net.Gabou.projectatmosphere.client;
-
-import com.mojang.blaze3d.pipeline.RenderTarget;
-import com.mojang.blaze3d.shaders.Uniform;
-import com.mojang.blaze3d.systems.RenderSystem;
-import com.mojang.blaze3d.vertex.*;
-import dev.nonamecrackers2.simpleclouds.client.renderer.SimpleCloudsRenderer;
-import dev.nonamecrackers2.simpleclouds.common.config.SimpleCloudsConfig;
-import net.Gabou.projectatmosphere.ProjectAtmosphere;
-import net.Gabou.projectatmosphere.modules.tornado.TornadoInstance;
-import net.Gabou.projectatmosphere.modules.tornado.TornadoManager;
-import net.Gabou.projectatmosphere.modules.tornado.TornadoLevel;
-import net.Gabou.projectatmosphere.particles.DebrisParticleData;
-import net.minecraft.client.Camera;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.multiplayer.ClientLevel;
-import net.minecraft.client.renderer.ShaderInstance;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.util.Mth;
-import net.minecraft.world.phys.Vec3;
-import org.joml.Matrix4f;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.lwjgl.opengl.GL11C.GL_BACK;
-import static org.lwjgl.opengl.GL11C.glCullFace;
-
-public class TornadoRenderHandler {
-
- private static final ResourceLocation NOISE_TEXTURE =
- ResourceLocation.fromNamespaceAndPath("projectatmosphere", "textures/effects/noise.png");
- private static final ResourceLocation TORNADO_TEXTURE =
- ResourceLocation.fromNamespaceAndPath("projectatmosphere", "textures/effects/base.png");
- private static final ResourceLocation FLOWMAP_TEXTURE =
- ResourceLocation.fromNamespaceAndPath("projectatmosphere", "textures/effects/flowmap.png");
- private static final ResourceLocation NORMALMAP_TEXTURE =
- ResourceLocation.fromNamespaceAndPath("projectatmosphere", "textures/effects/tornado_normal.png");
-
- private static final float SPAWN_DESCENT_DURATION = 10.0f;
-
-
- public static void renderTornado(PoseStack stack, double tornadoX, double tornadoY, double tornadoZ, float twistSpeed, ClientLevel level, Camera camera, Minecraft minecraft, TornadoInstance tornado) {
- ShaderInstance shader = MyShaders.TORNADO;
- if (shader == null) return;
-
-// // Bind the tornado’s base texture: prefer SimpleClouds’ cloud color target, fallback to static texture
-// AtomicBoolean boundBaseToClouds = new AtomicBoolean(false);
-// SimpleCloudsRenderer.getOptionalInstance().ifPresent(scr -> {
-// RenderTarget cloudRT = scr.getCloudTarget(); // offscreen clouds color
-// if (cloudRT == null) {
-// ProjectAtmosphere.LOGGER.warn("Cloud render target is null, cannot bind clouds as tornado base texture.");
-// return;
-// }
-// // Replace the base sampler (Sampler0) with the cloud texture and also expose it as CloudScene
-// shader.setSampler("Sampler0", cloudRT);
-// shader.setSampler("CloudScene", cloudRT);
-//
-// // Pass size so we can compute screen-space UVs in the shader
-// Uniform u = shader.getUniform("ScreenSizeX");
-// Uniform u1 = shader.getUniform("ScreenSizeY");
-// if (u != null && u1 != null) {
-// u.set((float) cloudRT.width);
-// u1.set((float) cloudRT.height);
-// }
-// boundBaseToClouds.set(true);
-// });
-//
-// if (!boundBaseToClouds.get()) {
- // Bind the tornado base texture: prefer SimpleClouds' cloud color target, fallback to static texture
- AtomicBoolean boundBaseToClouds = new AtomicBoolean(false);
- SimpleCloudsRenderer.getOptionalInstance().ifPresent(scr -> {
- RenderTarget cloudRT = scr.getCloudTarget();
- if (cloudRT == null) {
- ProjectAtmosphere.LOGGER.warn("Cloud render target is null, cannot bind clouds as tornado base texture.");
- return;
- }
- shader.setSampler("Sampler0", cloudRT);
- shader.setSampler("CloudScene", cloudRT);
-
- Uniform u = shader.getUniform("ScreenSizeX");
- Uniform u1 = shader.getUniform("ScreenSizeY");
- if (u != null && u1 != null) {
- u.set((float) cloudRT.width);
- u1.set((float) cloudRT.height);
- }
- boundBaseToClouds.set(true);
- });
-
- if (!boundBaseToClouds.get()) {
- RenderSystem.setShaderTexture(0, TORNADO_TEXTURE);
- RenderSystem.setShaderTexture(4, TORNADO_TEXTURE);
- }
- RenderSystem.setShaderTexture(1, FLOWMAP_TEXTURE);
- RenderSystem.setShaderTexture(2, NORMALMAP_TEXTURE);
- RenderSystem.setShaderTexture(3, NOISE_TEXTURE);
- RenderSystem.setShader(() -> shader);
- shader.apply();
- int segments = 64;
- int rings = 128;
- float baseRadius = 20f;
- float topRadius = 5f;
- float height = 356f;
- stack.pushPose();
- stack.translate(tornadoX, tornadoY, tornadoZ);
-
- Matrix4f matrix = stack.last().pose();
-
-
- var modelView = shader.getUniform("ModelViewMat");
- if (modelView != null) modelView.set(matrix);
-
- var projMat = shader.getUniform("ProjMat");
- if (projMat != null) projMat.set(RenderSystem.getProjectionMatrix());
-
- var timeUniform = shader.getUniform("Time");
- if (timeUniform != null) timeUniform.set(TornadoManager.getShaderTime());
-
- var twistUniform = shader.getUniform("TwistSpeed");
- if (twistUniform != null) twistUniform.set(twistSpeed);
-
- var baseRadiusUniform = shader.getUniform("BaseRadius");
- if (baseRadiusUniform != null) baseRadiusUniform.set(baseRadius);
-
- var topRadiusUniform = shader.getUniform("TopRadius");
- if (topRadiusUniform != null) topRadiusUniform.set(topRadius);
-
- var heightUniform = shader.getUniform("Height");
- if (heightUniform != null) heightUniform.set(height);
-
-
- var dustUniform = shader.getUniform("DustIntensity");
- if (dustUniform != null) dustUniform.set(0.5F);
-
- var coreUniform = shader.getUniform("CoreTightness");
- if (coreUniform != null) coreUniform.set(0.2f);
-
- var flowIntensity = shader.getUniform("FlowIntensity");
- if (flowIntensity != null) flowIntensity.set(0.1f);
-
- var scaleUniform = shader.getUniform("Scale");
- if (scaleUniform != null) {
- float scale = (float) (tornado.getLevel().getBaseDamage() / TornadoLevel.F1.getBaseDamage());
- scaleUniform.set(scale);
- }
-
-
- float partialTicks = minecraft.getFrameTime();
- float sunAngle = level.getTimeOfDay(partialTicks);
- float angle = sunAngle * ((float) Math.PI * 2.0F);
-
-
- float xLight = Mth.cos(angle);
- float yLight = Mth.sin(angle);
- float zLight = 0.2f;
-
-
- float length = Mth.sqrt(xLight * xLight + yLight * yLight + zLight * zLight);
- xLight /= length;
- yLight /= length;
- zLight /= length;
-
-
- var lightX = shader.getUniform("LightDirX");
- var lightY = shader.getUniform("LightDirY");
- var lightZ = shader.getUniform("LightDirZ");
-
- if (lightX != null) lightX.set(xLight);
- if (lightY != null) lightY.set(yLight);
- if (lightZ != null) lightZ.set(zLight);
-
-
- RenderSystem.enableBlend();
- RenderSystem.defaultBlendFunc();
- RenderSystem.disableCull();
- RenderSystem.enableDepthTest();
- RenderSystem.depthMask(true);
-
-
- Tesselator tess = Tesselator.getInstance();
- BufferBuilder buffer = tess.getBuilder();
- buffer.begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION_TEX);
-
-
- float windSpeed = tornado.wind.gustSpeed();
- float windAngleDeg = tornado.wind.angleRadians();
- float windAngleRad = (float) Math.toRadians(windAngleDeg);
-
- double windX = Math.cos(windAngleRad) * windSpeed;
- double windZ = Math.sin(windAngleRad) * windSpeed;
-
- Vec3 horizontalWind = new Vec3(windX, 5, windZ);
-
-
- float spawnProgress = Mth.clamp(tornado.getLifetimeSeconds() / SPAWN_DESCENT_DURATION, 0f, 1f);
- float cutoffY = height * (1.0f - spawnProgress);
-
- float time = TornadoManager.getShaderTime();
-
- for (int i = rings - 1; i >= 0; i--) {
- float y0 = i * (height / rings);
- float y1 = (i + 1) * (height / rings);
- if (y1 < cutoffY) {
- break;
- }
- if (y0 < cutoffY) {
- y0 = cutoffY;
- }
-
- float t0 = y0 / height;
- float t1 = y1 / height;
-
-
- for (int j = 0; j < segments; j++) {
- float u0 = j / (float) segments;
- float u1 = (j + 1f) / segments;
-
- final float U_EPS = 1e-6f;
- float u0s = (j == 0) ? (u0 + U_EPS) : u0;
- float u1s = (j == segments - 1) ? (1f - U_EPS) : u1;
-
- float twist = (float) (Math.PI * 3.5);
- float angleOffset0 = twist * (1 - t0);
- float angleOffset1 = twist * (1 - t1);
-
- float angle0_0 = (float) (2 * Math.PI * u0 + angleOffset0);
- float angle0_1 = (float) (2 * Math.PI * u1 + angleOffset0);
- float angle1_0 = (float) (2 * Math.PI * u0 + angleOffset1);
- float angle1_1 = (float) (2 * Math.PI * u1 + angleOffset1);
-
-
- float x00 = tornadoShapeRadius(y0, angle0_0, time) * (float) Math.cos(angle0_0);
- float z00 = tornadoShapeRadius(y0, angle0_0, time) * (float) Math.sin(angle0_0);
- float x01 = tornadoShapeRadius(y0, angle0_1, time) * (float) Math.cos(angle0_1);
- float z01 = tornadoShapeRadius(y0, angle0_1, time) * (float) Math.sin(angle0_1);
- float x10 = tornadoShapeRadius(y1, angle1_0, time) * (float) Math.cos(angle1_0);
- float z10 = tornadoShapeRadius(y1, angle1_0, time) * (float) Math.sin(angle1_0);
- float x11 = tornadoShapeRadius(y1, angle1_1, time) * (float) Math.cos(angle1_1);
- float z11 = tornadoShapeRadius(y1, angle1_1, time) * (float) Math.sin(angle1_1);
-
-
- float wiggleFreq = 5f;
- float wiggleAmp = 0.5f;
-
- x00 += Math.sin(y0 * 0.1f + angle0_0 * wiggleFreq) * wiggleAmp;
- z00 += Math.cos(y0 * 0.1f + angle0_0 * wiggleFreq) * wiggleAmp;
- x01 += Math.sin(y0 * 0.1f + angle0_1 * wiggleFreq) * wiggleAmp;
- z01 += Math.cos(y0 * 0.1f + angle0_1 * wiggleFreq) * wiggleAmp;
- x10 += Math.sin(y1 * 0.1f + angle1_0 * wiggleFreq) * wiggleAmp;
- z10 += Math.cos(y1 * 0.1f + angle1_0 * wiggleFreq) * wiggleAmp;
- x11 += Math.sin(y1 * 0.1f + angle1_1 * wiggleFreq) * wiggleAmp;
- z11 += Math.cos(y1 * 0.1f + angle1_1 * wiggleFreq) * wiggleAmp;
-
- float epsilon = 0.0001f;
- float v0 = (y0 + epsilon) / height;
- float v1 = (y1 - epsilon) / height;
-
- float bendScale = 1.5f;
- float bendFactor0 = (y0 / height) * bendScale * windSpeed;
- float bendFactor1 = (y1 / height) * bendScale * windSpeed;
-
- float offsetX0 = (float) horizontalWind.x * bendFactor0;
- float offsetZ0 = (float) horizontalWind.z * bendFactor0;
- float offsetX1 = (float) horizontalWind.x * bendFactor1;
- float offsetZ1 = (float) horizontalWind.z * bendFactor1;
-
- x00 += offsetX0;
- z00 += offsetZ0;
- x01 += offsetX0;
- z01 += offsetZ0;
- x10 += offsetX1;
- z10 += offsetZ1;
- x11 += offsetX1;
- z11 += offsetZ1;
-
-
- buffer.vertex(matrix, x00, y0, z00).uv(u0s, v0).endVertex();
- buffer.vertex(matrix, x10, y1, z10).uv(u0s, v1).endVertex();
- buffer.vertex(matrix, x11, y1, z11).uv(u1s, v1).endVertex();
-
- buffer.vertex(matrix, x00, y0, z00).uv(u0s, v0).endVertex();
- buffer.vertex(matrix, x11, y1, z11).uv(u1s, v1).endVertex();
- buffer.vertex(matrix, x01, y0, z01).uv(u1s, v0).endVertex();
- }
- }
-
-
-// --- Cone-like bowl (frustum) above the top ---
- int bowlRings = 24; // mesh resolution
- float bowlHeight = 12f; // vertical size of the cap
- topRadius=topRadius-3f;
- float angleDeg = 18f; // or:
- float targetTopR = topRadius * 1.6f; // or:
- float factor = 1.6f;
- float p = 1.0f; // 1 = straight cone; 1.05–1.2 = cone-ish but slightly rounded
-
-// radius grows linearly with height → cone look
-// slope = how many blocks of radius per 1 block of height
- float flareSlope = 0.30f; // tune: 0.2 = subtle, 0.5 = wide cone
- float maxBowlRadius = topRadius + flareSlope * bowlHeight;
-
- for (int i = 0; i < bowlRings; i++) {
- float t0 = i / (float) bowlRings;
- float t1 = (i + 1f) / bowlRings;
-
- float y0 = height + t0 * bowlHeight;
- float y1 = height + t1 * bowlHeight;
-
- // Pure conical growth (straight sides)
- float r0 = coneRadiusByAngle(y0, height, topRadius, bowlHeight, angleDeg, p);
- float r1 = coneRadiusByAngle(y1, height, topRadius, bowlHeight, angleDeg, p);
-
- // OPTIONAL: "rounded cone" (very slight curvature, still cone-ish)
- // float p = 1.10f; // 1.0 = pure cone, >1 = even straighter near seam
- // r0 = topRadius + (maxBowlRadius - topRadius) * (float) Math.pow(t0, p);
- // r1 = topRadius + (maxBowlRadius - topRadius) * (float) Math.pow(t1, p);
-
- // Keep twist continuity with the funnel
- float twist = (float) (Math.PI * 3.5);
- float aOff0 = twist * (1f - Math.min(1f, y0 / height));
- float aOff1 = twist * (1f - Math.min(1f, y1 / height));
-
- for (int j = 0; j < segments; j++) {
- float u0 = j / (float) segments;
- float u1 = (j + 1f) / (float) segments;
-
- float a00 = (float) (2 * Math.PI * u0 + aOff0);
- float a01 = (float) (2 * Math.PI * u1 + aOff0);
- float a10 = (float) (2 * Math.PI * u0 + aOff1);
- float a11 = (float) (2 * Math.PI * u1 + aOff1);
-
- float x00 = r0 * (float) Math.cos(a00);
- float z00 = r0 * (float) Math.sin(a00);
- float x01 = r0 * (float) Math.cos(a01);
- float z01 = r0 * (float) Math.sin(a01);
- float x10 = r1 * (float) Math.cos(a10);
- float z10 = r1 * (float) Math.sin(a10);
- float x11 = r1 * (float) Math.cos(a11);
- float z11 = r1 * (float) Math.sin(a11);
-
- // Optional: same bend so the bowl leans with wind
- float b0 = (y0 / (height + bowlHeight)) * 1.5f * windSpeed;
- float b1 = (y1 / (height + bowlHeight)) * 1.5f * windSpeed;
- x00 += (float) horizontalWind.x * b0;
- z00 += (float) horizontalWind.z * b0;
- x01 += (float) horizontalWind.x * b0;
- z01 += (float) horizontalWind.z * b0;
- x10 += (float) horizontalWind.x * b1;
- z10 += (float) horizontalWind.z * b1;
- x11 += (float) horizontalWind.x * b1;
- z11 += (float) horizontalWind.z * b1;
-
- // Clamp V near 1 to avoid stretching if your shader expects 0..1
- float epsilon = 1e-4f;
- float v0 = Math.min(1f - epsilon, y0 / height);
- float v1 = Math.min(1f - epsilon, y1 / height);
-
- buffer.vertex(matrix, x00, y0, z00).uv(u0, v0).endVertex();
- buffer.vertex(matrix, x10, y1, z10).uv(u0, v1).endVertex();
- buffer.vertex(matrix, x11, y1, z11).uv(u1, v1).endVertex();
-
- buffer.vertex(matrix, x00, y0, z00).uv(u0, v0).endVertex();
- buffer.vertex(matrix, x11, y1, z11).uv(u1, v1).endVertex();
- buffer.vertex(matrix, x01, y0, z01).uv(u1, v0).endVertex();
- }
- }
-
-
- tess.end();
- RenderSystem.enableCull();
- RenderSystem.disableBlend();
- stack.popPose();
- }
-
- public static void spawnDebrisParticles(TornadoInstance tornado, ClientLevel level) {
- for (int i = 0; i < 10; i++) {
- double maxRadius = 16.0;
- double radius = Math.sqrt(level.random.nextDouble()) * maxRadius;
- double height = level.random.nextDouble() * SimpleCloudsConfig.CLIENT.cloudHeight.get();
- float angularSpeed = 4f;
-
- level.addParticle(new DebrisParticleData(tornado, radius, height, angularSpeed),
- tornado.position.x, tornado.position.y, tornado.position.z, 0, 0.01, 0);
- }
- }
-
-
-
- private static float tornadoShapeRadius(float y, float angle, float time) {
- float yAdj = y + 45.0f;
- float zcurve = (float) Math.pow(yAdj, 1.5f) * 0.03f;
- float base = zcurve + 5.5f;
- float scale = Mth.clamp(zcurve * 0.2f, 0.1f, 1.0f);
- float radius = base + scale * Mth.sin((time - Mth.sqrt(yAdj)) + angle) * 5.0f;
- float ridgedNoise = 1.0f - 2.0f * Math.abs(Mth.sin((time * 1.5f + 0.1f * yAdj) + angle));
- radius -= ridgedNoise * 1.2f;
- return radius;
- }
-
- /**
- * Cone cap radius by specifying a *cone angle* (degrees).
- * angleDeg = 0..89 (slope = tan(angleDeg))
- * p = 1 for pure cone; >1 slightly round; <1 flares faster.
- */
- static float coneRadiusByAngle(float y, float seamY, float topRadius, float bowlHeight,
- float angleDeg, float p) {
- float t = Mth.clamp((y - seamY) / bowlHeight, 0f, 1f);
- float slope = (float) Math.tan(Math.toRadians(angleDeg));
- float targetR = topRadius + slope * bowlHeight;
- float linear = topRadius + (targetR - topRadius) * t;
- return p == 1f ? linear : topRadius + (targetR - topRadius) * (float) Math.pow(t, p);
- }
-
-
-}
-
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/TornadoShaders.java b/src/main/java/net/Gabou/projectatmosphere/client/TornadoShaders.java
deleted file mode 100644
index bea52098..00000000
--- a/src/main/java/net/Gabou/projectatmosphere/client/TornadoShaders.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package net.Gabou.projectatmosphere.client;
-
-import com.mojang.blaze3d.vertex.DefaultVertexFormat;
-import net.Gabou.projectatmosphere.ProjectAtmosphere;
-import net.minecraft.client.renderer.ShaderInstance;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.client.event.RegisterShadersEvent;
-import net.minecraftforge.eventbus.api.SubscribeEvent;
-import net.minecraftforge.fml.common.Mod;
-
-import java.io.IOException;
-
-@Mod.EventBusSubscriber(modid = ProjectAtmosphere.MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
-public class TornadoShaders {
-
-
- @SubscribeEvent
- public static void onRegisterShaders(RegisterShadersEvent event) throws IOException {
- ShaderInstance shader = new ShaderInstance(event.getResourceProvider(),
- ResourceLocation.fromNamespaceAndPath(ProjectAtmosphere.MODID, "tornado"), DefaultVertexFormat.POSITION_TEX);
- event.registerShader(shader, s -> MyShaders.TORNADO = s);
- ShaderInstance shader1 = new ShaderInstance(event.getResourceProvider(),
- ResourceLocation.fromNamespaceAndPath(ProjectAtmosphere.MODID, "box_tornado"), DefaultVertexFormat.POSITION_TEX);
- event.registerShader(shader1, s -> MyShaders.BOX_TORNADO = s);
- }
-
-
-}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/atmosphere/AtmosphereClientState.java b/src/main/java/net/Gabou/projectatmosphere/client/atmosphere/AtmosphereClientState.java
new file mode 100644
index 00000000..3b03e194
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/atmosphere/AtmosphereClientState.java
@@ -0,0 +1,167 @@
+package net.Gabou.projectatmosphere.client.atmosphere;
+
+import dev.nonamecrackers2.simpleclouds.common.world.CloudManager;
+import net.Gabou.projectatmosphere.client.fog.FogBiomeClassifier;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.core.BlockPos;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.Level;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+
+@OnlyIn(Dist.CLIENT)
+public final class AtmosphereClientState {
+ private static final float HUMIDITY_TRACKING = 0.18F;
+ private static final float RAIN_TRACKING = 0.22F;
+ private static final float CLOUD_TRACKING = 0.16F;
+ private static final float CLEARING_TRACKING = 0.20F;
+ private static final float RECENT_RAIN_GAIN = 0.14F;
+ private static final float RECENT_RAIN_DECAY = 0.0045F;
+ private static final int SERVER_SAMPLE_STALE_TICKS = 120;
+
+ private static float targetHumidityPercent;
+ private static float visualHumidityPercent;
+ private static float targetRainIntensity;
+ private static float visualRainIntensity;
+ private static float targetCloudCover;
+ private static float visualCloudCover;
+ private static float recentRainFactor;
+ private static float clearingTrend;
+ private static boolean hasServerSample;
+ private static int serverSampleAgeTicks;
+ private static ResourceKey lastDimension;
+
+ private AtmosphereClientState() {
+ }
+
+ public static void applyServerUpdate(float humidityPercent, float rainIntensity, float cloudCover) {
+ hasServerSample = true;
+ serverSampleAgeTicks = 0;
+ targetHumidityPercent = Mth.clamp(humidityPercent, 0.0F, 100.0F);
+ targetRainIntensity = Mth.clamp(rainIntensity, 0.0F, 1.0F);
+ targetCloudCover = Mth.clamp(cloudCover, 0.0F, 1.0F);
+ }
+
+ public static void tick(Minecraft minecraft) {
+ ClientLevel level = minecraft.level;
+ if (level == null || minecraft.player == null) {
+ clear();
+ return;
+ }
+
+ ResourceKey dimension = level.dimension();
+ if (lastDimension != null && !lastDimension.equals(dimension)) {
+ clearVisuals();
+ hasServerSample = false;
+ serverSampleAgeTicks = 0;
+ }
+ lastDimension = dimension;
+
+ if (serverSampleAgeTicks < Integer.MAX_VALUE) {
+ serverSampleAgeTicks++;
+ }
+ if (serverSampleAgeTicks > SERVER_SAMPLE_STALE_TICKS) {
+ hasServerSample = false;
+ }
+
+ BlockPos pos = minecraft.player.blockPosition();
+ float fallbackHumidity = FogBiomeClassifier.computeFallbackHumidityPercent(level, pos);
+ float fallbackRain = FogBiomeClassifier.computeClientRainIntensity(level, pos);
+ float fallbackCloud = estimateFallbackCloudCover(level, pos, fallbackRain);
+
+ if (!Level.OVERWORLD.equals(dimension)) {
+ targetHumidityPercent = 0.0F;
+ targetRainIntensity = 0.0F;
+ targetCloudCover = 0.0F;
+ hasServerSample = false;
+ } else if (!hasServerSample) {
+ targetHumidityPercent = fallbackHumidity;
+ targetRainIntensity = fallbackRain;
+ targetCloudCover = fallbackCloud;
+ } else {
+ targetHumidityPercent = Mth.clamp(targetHumidityPercent, 0.0F, 100.0F);
+ targetRainIntensity = Math.max(Mth.clamp(targetRainIntensity, 0.0F, 1.0F), fallbackRain * 0.75F);
+ targetCloudCover = Math.max(Mth.clamp(targetCloudCover, 0.0F, 1.0F), fallbackCloud * 0.65F);
+ }
+
+ float previousCloudCover = visualCloudCover;
+ visualHumidityPercent = Mth.lerp(HUMIDITY_TRACKING, visualHumidityPercent, targetHumidityPercent);
+ visualRainIntensity = Mth.lerp(RAIN_TRACKING, visualRainIntensity, targetRainIntensity);
+ visualCloudCover = Mth.lerp(CLOUD_TRACKING, visualCloudCover, targetCloudCover);
+
+ float cloudClearingSample = Mth.clamp((previousCloudCover - visualCloudCover) * 7.5F, 0.0F, 1.0F);
+ clearingTrend = Mth.lerp(CLEARING_TRACKING, clearingTrend, cloudClearingSample);
+
+ if (visualRainIntensity > 0.05F) {
+ recentRainFactor = Mth.clamp(
+ Math.max(recentRainFactor, visualRainIntensity) + (visualRainIntensity * RECENT_RAIN_GAIN),
+ 0.0F,
+ 1.0F
+ );
+ } else {
+ recentRainFactor = Math.max(0.0F, recentRainFactor - RECENT_RAIN_DECAY);
+ }
+ }
+
+ public static Snapshot getSnapshot() {
+ return new Snapshot(
+ visualHumidityPercent,
+ visualRainIntensity,
+ visualCloudCover,
+ recentRainFactor,
+ clearingTrend
+ );
+ }
+
+ public static float getRainIntensity() {
+ return visualRainIntensity;
+ }
+
+ public static float getCloudCover() {
+ return visualCloudCover;
+ }
+
+ public static float getHumidityPercent() {
+ return visualHumidityPercent;
+ }
+
+ private static float estimateFallbackCloudCover(ClientLevel level, BlockPos pos, float fallbackRain) {
+ try {
+ return CloudManager.get(level).getCloudGenerator().getCloudAtWorldPosition(pos.getX() + 0.5F, pos.getZ() + 0.5F) != null
+ ? Math.max(0.65F, fallbackRain)
+ : (fallbackRain > 0.0F ? 0.72F : 0.0F);
+ } catch (Exception ignored) {
+ return fallbackRain > 0.0F ? 0.72F : 0.0F;
+ }
+ }
+
+ private static void clearVisuals() {
+ targetHumidityPercent = 0.0F;
+ visualHumidityPercent = 0.0F;
+ targetRainIntensity = 0.0F;
+ visualRainIntensity = 0.0F;
+ targetCloudCover = 0.0F;
+ visualCloudCover = 0.0F;
+ recentRainFactor = 0.0F;
+ clearingTrend = 0.0F;
+ }
+
+ private static void clear() {
+ clearVisuals();
+ hasServerSample = false;
+ serverSampleAgeTicks = 0;
+ lastDimension = null;
+ }
+
+ public record Snapshot(
+ float humidityPercent,
+ float rainIntensity,
+ float cloudCover,
+ float recentRainFactor,
+ float clearingTrend
+ ) {
+ public static final Snapshot NONE = new Snapshot(0.0F, 0.0F, 0.0F, 0.0F, 0.0F);
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/crash/ProjectAtmosphereCrashHandler.java b/src/main/java/net/Gabou/projectatmosphere/client/crash/ProjectAtmosphereCrashHandler.java
new file mode 100644
index 00000000..158d80d2
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/crash/ProjectAtmosphereCrashHandler.java
@@ -0,0 +1,271 @@
+package net.Gabou.projectatmosphere.client.crash;
+
+import net.Gabou.projectatmosphere.ProjectAtmosphere;
+import net.Gabou.projectatmosphere.client.screen.ProjectAtmosphereCrashScreen;
+import net.minecraft.CrashReport;
+import net.minecraft.SharedConstants;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.screens.Screen;
+import net.minecraftforge.fml.ModList;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.nio.file.Path;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.Locale;
+import java.util.Set;
+
+public final class ProjectAtmosphereCrashHandler {
+ private static final String PROJECT_ATMOSPHERE_PACKAGE = "net.Gabou.projectatmosphere";
+ private static final String DISCORD_INVITE_URL = "https://discord.gg/2jRhTJgYz4";
+ private static final DateTimeFormatter REPORT_TIMESTAMP = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT);
+
+ @Nullable
+ private static CrashContext activeCrash;
+
+ private ProjectAtmosphereCrashHandler() {
+ }
+
+ public static boolean handleThrowable(Minecraft minecraft, Throwable throwable, String title) {
+ if (!isProjectAtmosphereCrash(throwable)) {
+ return false;
+ }
+
+ return handleCrashReport(minecraft, CrashReport.forThrowable(throwable, title));
+ }
+
+ public static boolean handleCrashReport(Minecraft minecraft, CrashReport report) {
+ if (!isProjectAtmosphereCrash(report)) {
+ return false;
+ }
+
+ if (activeCrash != null && minecraft.screen instanceof ProjectAtmosphereCrashScreen) {
+ return true;
+ }
+
+ CrashReport enrichedReport = enrichReport(minecraft, report);
+ CrashContext crashContext = buildCrashContext(minecraft, enrichedReport);
+ activeCrash = crashContext;
+
+ ProjectAtmosphere.LOGGER.error("Intercepted a Project Atmosphere client crash; opening the custom crash screen.");
+
+ Screen crashScreen = new ProjectAtmosphereCrashScreen(crashContext);
+ presentCrashScreen(minecraft, crashScreen);
+ return true;
+ }
+
+ public static String getDiscordInviteUrl() {
+ return DISCORD_INVITE_URL;
+ }
+
+ @Nullable
+ public static CrashContext getActiveCrash() {
+ return activeCrash;
+ }
+
+ public static boolean isProjectAtmosphereCrash(CrashReport report) {
+ return isProjectAtmosphereCrash(report.getException());
+ }
+
+ public static boolean isProjectAtmosphereCrash(@Nullable Throwable throwable) {
+ if (throwable == null) {
+ return false;
+ }
+
+ Set visited = Collections.newSetFromMap(new IdentityHashMap<>());
+ Deque queue = new ArrayDeque<>();
+ queue.add(throwable);
+
+ while (!queue.isEmpty()) {
+ Throwable current = queue.removeFirst();
+ if (!visited.add(current)) {
+ continue;
+ }
+
+ if (isProjectAtmosphereClass(current.getClass().getName()) || containsProjectAtmosphereFrame(current.getStackTrace())) {
+ return true;
+ }
+
+ Throwable cause = current.getCause();
+ if (cause != null) {
+ queue.addLast(cause);
+ }
+
+ for (Throwable suppressed : current.getSuppressed()) {
+ if (suppressed != null) {
+ queue.addLast(suppressed);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static void presentCrashScreen(Minecraft minecraft, Screen crashScreen) {
+ try {
+ if (minecraft.level != null && tryTransitionMethod(minecraft, "clearClientLevel", crashScreen)) {
+ return;
+ }
+
+ if (minecraft.level != null && tryTransitionMethod(minecraft, "disconnect", crashScreen)) {
+ return;
+ }
+
+ if (minecraft.level == null || minecraft.screen != crashScreen) {
+ minecraft.setScreen(crashScreen);
+ }
+ } catch (Throwable secondaryFailure) {
+ ProjectAtmosphere.LOGGER.error("Failed to clear the client level while opening the Project Atmosphere crash screen.", secondaryFailure);
+ minecraft.setScreen(crashScreen);
+ }
+ }
+
+ private static boolean tryTransitionMethod(Minecraft minecraft, String methodName, Screen crashScreen) {
+ try {
+ Minecraft.class.getMethod(methodName, Screen.class).invoke(minecraft, crashScreen);
+ return true;
+ } catch (ReflectiveOperationException ignored) {
+ return false;
+ }
+ }
+
+ private static CrashReport enrichReport(Minecraft minecraft, CrashReport report) {
+ try {
+ return minecraft.fillReport(report);
+ } catch (Throwable secondaryFailure) {
+ ProjectAtmosphere.LOGGER.error("Failed to enrich the Project Atmosphere crash report; falling back to the original report.", secondaryFailure);
+ return report;
+ }
+ }
+
+ private static CrashContext buildCrashContext(Minecraft minecraft, CrashReport report) {
+ Throwable throwable = report.getException();
+ StackTraceElement firstRelevantFrame = findFirstProjectAtmosphereFrame(throwable);
+ Path reportPath = saveCrashReport(minecraft, report);
+ String modVersion = ModList.get().getModContainerById(ProjectAtmosphere.MODID)
+ .map(container -> container.getModInfo().getVersion().toString())
+ .orElse("unknown");
+ String supportSummary = buildSupportSummary(report, throwable, firstRelevantFrame, reportPath, modVersion);
+
+ return new CrashContext(
+ report.getTitle(),
+ formatThrowable(throwable),
+ firstRelevantFrame == null ? "Unavailable" : firstRelevantFrame.toString(),
+ reportPath == null ? "Unable to save crash report" : reportPath.toAbsolutePath().toString(),
+ supportSummary
+ );
+ }
+
+ @Nullable
+ private static Path saveCrashReport(Minecraft minecraft, CrashReport report) {
+ File existingSave = report.getSaveFile();
+ if (existingSave != null) {
+ return existingSave.toPath();
+ }
+
+ File reportFile = new File(
+ new File(minecraft.gameDirectory, "crash-reports"),
+ "crash-" + REPORT_TIMESTAMP.format(LocalDateTime.now()) + "-projectatmosphere-client.txt"
+ );
+
+ return report.saveToFile(reportFile) ? reportFile.toPath() : null;
+ }
+
+ @Nullable
+ private static StackTraceElement findFirstProjectAtmosphereFrame(@Nullable Throwable throwable) {
+ if (throwable == null) {
+ return null;
+ }
+
+ Set visited = Collections.newSetFromMap(new IdentityHashMap<>());
+ Deque queue = new ArrayDeque<>();
+ queue.add(throwable);
+
+ while (!queue.isEmpty()) {
+ Throwable current = queue.removeFirst();
+ if (!visited.add(current)) {
+ continue;
+ }
+
+ for (StackTraceElement element : current.getStackTrace()) {
+ if (isProjectAtmosphereClass(element.getClassName())) {
+ return element;
+ }
+ }
+
+ Throwable cause = current.getCause();
+ if (cause != null) {
+ queue.addLast(cause);
+ }
+
+ for (Throwable suppressed : current.getSuppressed()) {
+ if (suppressed != null) {
+ queue.addLast(suppressed);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean containsProjectAtmosphereFrame(StackTraceElement[] stackTrace) {
+ for (StackTraceElement element : stackTrace) {
+ if (isProjectAtmosphereClass(element.getClassName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isProjectAtmosphereClass(String className) {
+ return className != null && className.startsWith(PROJECT_ATMOSPHERE_PACKAGE);
+ }
+
+ private static String buildSupportSummary(
+ CrashReport report,
+ Throwable throwable,
+ @Nullable StackTraceElement firstRelevantFrame,
+ @Nullable Path reportPath,
+ String modVersion
+ ) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Project Atmosphere support request").append('\n');
+ builder.append("Mod version: ").append(modVersion).append('\n');
+ builder.append("Minecraft version: ").append(SharedConstants.getCurrentVersion().getName()).append('\n');
+ builder.append("Crash title: ").append(report.getTitle()).append('\n');
+ builder.append("Exception: ").append(formatThrowable(throwable)).append('\n');
+ builder.append("Project Atmosphere frame: ")
+ .append(firstRelevantFrame == null ? "Unavailable" : firstRelevantFrame)
+ .append('\n');
+ builder.append("Crash report: ")
+ .append(reportPath == null ? "Unable to save crash report" : reportPath.toAbsolutePath())
+ .append('\n');
+ builder.append("Discord: ").append(DISCORD_INVITE_URL);
+ return builder.toString();
+ }
+
+ private static String formatThrowable(@Nullable Throwable throwable) {
+ if (throwable == null) {
+ return "Unknown throwable";
+ }
+
+ String message = throwable.getMessage();
+ String name = throwable.getClass().getName();
+ return message == null || message.isBlank() ? name : name + ": " + message;
+ }
+
+ public record CrashContext(
+ String crashTitle,
+ String exceptionSummary,
+ String projectAtmosphereFrame,
+ String savedReportPath,
+ String supportSummary
+ ) {
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/fog/AtmosphereFogState.java b/src/main/java/net/Gabou/projectatmosphere/client/fog/AtmosphereFogState.java
new file mode 100644
index 00000000..41a182a5
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/fog/AtmosphereFogState.java
@@ -0,0 +1,150 @@
+package net.Gabou.projectatmosphere.client.fog;
+
+import net.Gabou.projectatmosphere.config.AtmoCommonConfig;
+import net.Gabou.projectatmosphere.modules.fog.FogHeuristics;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.core.BlockPos;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.Vec3;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+
+@OnlyIn(Dist.CLIENT)
+public final class AtmosphereFogState {
+ private static final float HUMIDITY_TRACKING = 0.18F;
+ private static final float RAIN_TRACKING = 0.22F;
+ private static final float BIOME_TRACKING = 0.14F;
+ private static final float DEBUG_TRACKING = 0.22F;
+
+ private static float targetHumidityPercent;
+ private static float visualHumidityPercent;
+ private static float targetRainIntensity;
+ private static float visualRainIntensity;
+ private static float targetWetBiomeFactor;
+ private static float visualWetBiomeFactor;
+ private static float targetDebugStrength;
+ private static float visualDebugStrength;
+ private static int debugOverrideTicks;
+ private static boolean hasServerSample;
+ private static ResourceKey lastDimension;
+
+ private AtmosphereFogState() {
+ }
+
+ public static void applyServerUpdate(float humidityPercent, float rainIntensity) {
+ hasServerSample = true;
+ targetHumidityPercent = Mth.clamp(humidityPercent, 0.0F, 100.0F);
+ targetRainIntensity = Mth.clamp(rainIntensity, 0.0F, 1.0F);
+ }
+
+ public static void applyDebugOverride(float strength, int durationTicks) {
+ targetDebugStrength = Mth.clamp(strength, 0.0F, 1.0F);
+ debugOverrideTicks = Math.max(durationTicks, 0);
+ }
+
+ public static void clearDebugOverride() {
+ debugOverrideTicks = 0;
+ targetDebugStrength = 0.0F;
+ }
+
+ public static void tick(Minecraft minecraft) {
+ tickDebugOverride();
+ boolean fogEnabled = AtmoCommonConfig.FOG_ENABLED.get();
+ boolean debugActive = hasDebugOverride();
+ if (!fogEnabled && !debugActive) {
+ clear();
+ return;
+ }
+
+ ClientLevel level = minecraft.level;
+ if (level == null || minecraft.player == null) {
+ clear();
+ return;
+ }
+
+ ResourceKey dimension = level.dimension();
+ if (lastDimension != null && !lastDimension.equals(dimension)) {
+ clearVisuals();
+ hasServerSample = false;
+ }
+ lastDimension = dimension;
+
+ if (!fogEnabled || !Level.OVERWORLD.equals(dimension)) {
+ targetHumidityPercent = 0.0F;
+ targetRainIntensity = 0.0F;
+ targetWetBiomeFactor = 0.0F;
+ hasServerSample = false;
+ trackVisuals();
+ return;
+ }
+
+ BlockPos pos = minecraft.player.blockPosition();
+ targetWetBiomeFactor = FogBiomeClassifier.computeWetBiomeFactor(level, pos);
+ if (!hasServerSample) {
+ targetHumidityPercent = FogBiomeClassifier.computeFallbackHumidityPercent(level, pos);
+ targetRainIntensity = FogBiomeClassifier.computeClientRainIntensity(level, pos);
+ } else {
+ targetRainIntensity = Math.max(targetRainIntensity, FogBiomeClassifier.computeClientRainIntensity(level, pos) * 0.75F);
+ }
+ trackVisuals();
+ }
+
+ public static FogHeuristics.FogProfile sample(ClientLevel level, Vec3 cameraPos, float partialTick) {
+ FogHeuristics.FogProfile debugProfile = FogHeuristics.debugSample(visualDebugStrength);
+ boolean fogEnabled = AtmoCommonConfig.FOG_ENABLED.get();
+ if (level == null) {
+ return debugProfile;
+ }
+
+ if (!fogEnabled || !Level.OVERWORLD.equals(level.dimension())) {
+ return debugProfile;
+ }
+
+ BlockPos samplePos = BlockPos.containing(cameraPos);
+ float localWetBiome = Math.max(visualWetBiomeFactor, FogBiomeClassifier.computeWetBiomeFactor(level, samplePos));
+ float localRain = Math.max(visualRainIntensity, FogBiomeClassifier.computeClientRainIntensity(level, samplePos));
+ FogHeuristics.FogProfile liveProfile = FogHeuristics.sample(visualHumidityPercent, localWetBiome, localRain);
+ return FogHeuristics.max(liveProfile, debugProfile);
+ }
+
+ private static void trackVisuals() {
+ visualHumidityPercent = Mth.lerp(HUMIDITY_TRACKING, visualHumidityPercent, targetHumidityPercent);
+ visualRainIntensity = Mth.lerp(RAIN_TRACKING, visualRainIntensity, targetRainIntensity);
+ visualWetBiomeFactor = Mth.lerp(BIOME_TRACKING, visualWetBiomeFactor, targetWetBiomeFactor);
+ visualDebugStrength = Mth.lerp(DEBUG_TRACKING, visualDebugStrength, targetDebugStrength);
+ }
+
+ private static void tickDebugOverride() {
+ if (debugOverrideTicks > 0) {
+ debugOverrideTicks--;
+ if (debugOverrideTicks == 0) {
+ targetDebugStrength = 0.0F;
+ }
+ }
+ }
+
+ private static boolean hasDebugOverride() {
+ return debugOverrideTicks > 0 || targetDebugStrength > 0.001F || visualDebugStrength > 0.001F;
+ }
+
+ private static void clearVisuals() {
+ targetHumidityPercent = 0.0F;
+ visualHumidityPercent = 0.0F;
+ targetRainIntensity = 0.0F;
+ visualRainIntensity = 0.0F;
+ targetWetBiomeFactor = 0.0F;
+ visualWetBiomeFactor = 0.0F;
+ targetDebugStrength = 0.0F;
+ visualDebugStrength = 0.0F;
+ debugOverrideTicks = 0;
+ }
+
+ private static void clear() {
+ clearVisuals();
+ hasServerSample = false;
+ lastDimension = null;
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/fog/FogBiomeClassifier.java b/src/main/java/net/Gabou/projectatmosphere/client/fog/FogBiomeClassifier.java
new file mode 100644
index 00000000..e6518e61
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/fog/FogBiomeClassifier.java
@@ -0,0 +1,108 @@
+package net.Gabou.projectatmosphere.client.fog;
+
+import dev.nonamecrackers2.simpleclouds.common.world.CloudManager;
+import net.Gabou.projectatmosphere.config.AtmoCommonConfig;
+import net.minecraft.core.BlockPos;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.LevelReader;
+import net.minecraft.world.level.biome.Biome;
+
+import java.util.List;
+import java.util.Locale;
+
+public final class FogBiomeClassifier {
+ private FogBiomeClassifier() {
+ }
+
+ public static float computeWetBiomeFactor(LevelReader level, BlockPos pos) {
+ if (level == null || pos == null) {
+ return 0.0F;
+ }
+
+ Biome biome = level.getBiome(pos).value();
+ float downfallThreshold = AtmoCommonConfig.FOG_WET_BIOME_DOWNFALL_MIN.get().floatValue();
+ float factor = 0.0F;
+
+ if (biome.getModifiedClimateSettings().hasPrecipitation()) {
+ factor = Math.max(factor, remapClamped(biome.getModifiedClimateSettings().downfall(), downfallThreshold, 1.0F));
+ }
+
+ ResourceLocation biomeId = level.getBiome(pos).unwrapKey()
+ .map(key -> key.location())
+ .orElse(null);
+ if (biomeId == null) {
+ return Mth.clamp(factor, 0.0F, 1.0F);
+ }
+
+ String fullId = biomeId.toString().toLowerCase(Locale.ROOT);
+ String path = biomeId.getPath().toLowerCase(Locale.ROOT);
+
+ if (matchesExact(fullId, AtmoCommonConfig.FOG_WET_BIOME_IDS.get())) {
+ return 1.0F;
+ }
+ if (matchesKeyword(fullId, path, AtmoCommonConfig.FOG_WET_BIOME_KEYWORDS.get())) {
+ factor = Math.max(factor, 1.0F);
+ }
+
+ return Mth.clamp(factor, 0.0F, 1.0F);
+ }
+
+ public static float computeFallbackHumidityPercent(Level level, BlockPos pos) {
+ if (level == null || pos == null) {
+ return 0.0F;
+ }
+
+ Biome biome = level.getBiome(pos).value();
+ float humidity = biome.getModifiedClimateSettings().downfall() * 100.0F;
+ if (biome.getModifiedClimateSettings().hasPrecipitation() && isLocallyRaining(level, pos)) {
+ humidity = Math.max(humidity, 82.0F);
+ }
+ return Mth.clamp(humidity, 0.0F, 100.0F);
+ }
+
+ public static float computeClientRainIntensity(Level level, BlockPos pos) {
+ return isLocallyRaining(level, pos) ? 0.85F : 0.0F;
+ }
+
+ private static boolean isLocallyRaining(Level level, BlockPos pos) {
+ try {
+ return CloudManager.get(level).isRainingAt(pos);
+ } catch (Exception ignored) {
+ return level.isRainingAt(pos);
+ }
+ }
+
+ private static boolean matchesExact(String fullId, List extends String> configuredIds) {
+ for (String configured : configuredIds) {
+ if (configured != null && fullId.equals(configured.toLowerCase(Locale.ROOT))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean matchesKeyword(String fullId, String path, List extends String> keywords) {
+ for (String keyword : keywords) {
+ if (keyword == null) {
+ continue;
+ }
+ String lowered = keyword.toLowerCase(Locale.ROOT).trim();
+ if (lowered.isEmpty()) {
+ continue;
+ }
+ if (path.contains(lowered) || fullId.contains(lowered)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static float remapClamped(float value, float start, float end) {
+ if (end <= start) {
+ return value >= end ? 1.0F : 0.0F;
+ }
+ return Mth.clamp((value - start) / (end - start), 0.0F, 1.0F);
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/hurricane/ClientHurricaneStateCache.java b/src/main/java/net/Gabou/projectatmosphere/client/hurricane/ClientHurricaneStateCache.java
new file mode 100644
index 00000000..9808459c
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/hurricane/ClientHurricaneStateCache.java
@@ -0,0 +1,225 @@
+package net.Gabou.projectatmosphere.client.hurricane;
+
+import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import dev.nonamecrackers2.simpleclouds.common.cloud.region.CloudRegion;
+import net.Gabou.projectatmosphere.modules.hurricane.HurricaneSemantics;
+import net.Gabou.projectatmosphere.modules.hurricane.HurricaneManager;
+import net.Gabou.projectatmosphere.modules.hurricane.HurricaneRenderSnapshot;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.Mth;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public final class ClientHurricaneStateCache {
+ private static final int DEFAULT_BLEND_TICKS = 10;
+ private static final Map ENTRIES = new LinkedHashMap<>();
+ private static final Map RESERVATION_REGIONS = new LinkedHashMap<>();
+ private static long cachedSemanticTick = Long.MIN_VALUE;
+ private static float cachedSemanticPartialTick = Float.NaN;
+ private static List cachedSemanticSnapshots = List.of();
+
+ private ClientHurricaneStateCache() {
+ }
+
+ public static void applySnapshots(List snapshots) {
+ Minecraft mc = Minecraft.getInstance();
+ long clientTick = mc.level != null ? mc.level.getGameTime() : 0L;
+
+ Map next = new LinkedHashMap<>();
+ Map nextReservations = new LinkedHashMap<>();
+ for (HurricaneRenderSnapshot snapshot : snapshots) {
+ Entry previous = ENTRIES.get(snapshot.id());
+ if (previous == null) {
+ next.put(snapshot.id(), new Entry(snapshot, snapshot, clientTick));
+ } else {
+ next.put(snapshot.id(), new Entry(previous.current, snapshot, clientTick));
+ }
+ nextReservations.put(snapshot.id(), getReservationRegion(snapshot));
+ }
+
+ ENTRIES.clear();
+ ENTRIES.putAll(next);
+ RESERVATION_REGIONS.clear();
+ RESERVATION_REGIONS.putAll(nextReservations);
+ invalidateSemanticSnapshotCache();
+ }
+
+ public static void tick(ClientLevel level) {
+ if (level == null) {
+ clear();
+ }
+ }
+
+ public static void clear() {
+ ENTRIES.clear();
+ RESERVATION_REGIONS.clear();
+ invalidateSemanticSnapshotCache();
+ }
+
+ public static List getSemanticSnapshots() {
+ return getSemanticSnapshots(0.0F);
+ }
+
+ public static List getSemanticSnapshots(float partialTick) {
+ Minecraft mc = Minecraft.getInstance();
+ if (mc.level == null) {
+ return List.of();
+ }
+ long clientTick = mc.level.getGameTime();
+ if (clientTick == cachedSemanticTick && Float.compare(partialTick, cachedSemanticPartialTick) == 0) {
+ return cachedSemanticSnapshots;
+ }
+
+ List snapshots = ENTRIES.isEmpty()
+ ? projectatmosphere$getIntegratedServerSnapshots(mc)
+ : buildInterpolatedSnapshots(clientTick, partialTick);
+ cachedSemanticTick = clientTick;
+ cachedSemanticPartialTick = partialTick;
+ cachedSemanticSnapshots = snapshots;
+ return snapshots;
+ }
+
+ private static List buildInterpolatedSnapshots(long clientTick, float partialTick) {
+ List snapshots = new ArrayList<>(ENTRIES.size());
+ for (Entry entry : ENTRIES.values()) {
+ float blend = Mth.clamp(((float)(clientTick - entry.clientUpdateTick) + partialTick) / (float)DEFAULT_BLEND_TICKS, 0.0F, 1.0F);
+ HurricaneRenderSnapshot start = entry.previous;
+ HurricaneRenderSnapshot end = entry.current;
+
+ snapshots.add(new HurricaneRenderSnapshot(
+ end.id(),
+ Mth.lerp(blend, start.centerX(), end.centerX()),
+ Mth.lerp(blend, start.centerZ(), end.centerZ()),
+ Mth.lerp(blend, start.anchorY(), end.anchorY()),
+ Mth.lerp(blend, start.coreRadius(), end.coreRadius()),
+ Mth.lerp(blend, start.stormExtentRadius(), end.stormExtentRadius()),
+ Mth.lerp(blend, start.eyeRadius(), end.eyeRadius()),
+ Mth.lerp(blend, start.edgeFade(), end.edgeFade()),
+ end.bandCount(),
+ Mth.lerp(blend, start.bandWidth(), end.bandWidth()),
+ Mth.lerp(blend, start.spiralTightness(), end.spiralTightness()),
+ Mth.lerp(blend, start.rotationPhase(), end.rotationPhase()) + Mth.lerp(blend, start.rotationSpeed(), end.rotationSpeed()) * partialTick,
+ Mth.lerp(blend, start.rotationSpeed(), end.rotationSpeed()),
+ Mth.lerp(blend, start.transitionStart(), end.transitionStart()),
+ Mth.lerp(blend, start.transitionEnd(), end.transitionEnd()),
+ Mth.lerp(blend, start.normalizedIntensity(), end.normalizedIntensity()),
+ end.cloudTypeId(),
+ Mth.floor(Mth.lerp(blend, start.ageTicks(), end.ageTicks()) + partialTick)
+ ));
+ }
+ return snapshots;
+ }
+
+ public static boolean hasHurricanes() {
+ if (!ENTRIES.isEmpty()) {
+ return true;
+ }
+ Minecraft mc = Minecraft.getInstance();
+ if (mc.level == null || !mc.hasSingleplayerServer()) {
+ return false;
+ }
+ return !HurricaneManager.getActiveHurricanes().isEmpty();
+ }
+
+ public static CloudRegion getReservationRegion(HurricaneRenderSnapshot snapshot) {
+ CloudRegion region = RESERVATION_REGIONS.get(snapshot.id());
+ if (region == null) {
+ region = HurricaneSemantics.createReservationRegion(snapshot);
+ RESERVATION_REGIONS.put(snapshot.id(), region);
+ } else {
+ HurricaneSemantics.updateReservationRegion(region, snapshot);
+ }
+ return region;
+ }
+
+ private static List projectatmosphere$getIntegratedServerSnapshots(Minecraft mc) {
+ if (!mc.hasSingleplayerServer()) {
+ return List.of();
+ }
+ var server = mc.getSingleplayerServer();
+ if (server == null) {
+ return List.of();
+ }
+ List active = HurricaneManager.getActiveHurricanes();
+ if (active.isEmpty()) {
+ return List.of();
+ }
+ List snapshots = new ArrayList<>(active.size());
+ for (net.Gabou.projectatmosphere.modules.hurricane.HurricaneInstance hurricane : active) {
+ snapshots.add(hurricane.createRenderSnapshot());
+ }
+ return snapshots;
+ }
+
+ private static void invalidateSemanticSnapshotCache() {
+ cachedSemanticTick = Long.MIN_VALUE;
+ cachedSemanticPartialTick = Float.NaN;
+ cachedSemanticSnapshots = List.of();
+ }
+
+ public static List getRenderableHurricanes(float partialTick) {
+ Minecraft mc = Minecraft.getInstance();
+ if (mc.level == null) {
+ return List.of();
+ }
+
+ List snapshots = getSemanticSnapshots(partialTick);
+ List renderables = new ArrayList<>(snapshots.size());
+ for (HurricaneRenderSnapshot snapshot : snapshots) {
+ renderables.add(new RenderableHurricane(
+ snapshot.id(),
+ snapshot.centerX() / (double)SimpleCloudsConstants.CLOUD_SCALE,
+ snapshot.centerZ() / (double)SimpleCloudsConstants.CLOUD_SCALE,
+ snapshot.anchorY(),
+ snapshot.coreRadius() / (float)SimpleCloudsConstants.CLOUD_SCALE,
+ snapshot.stormExtentRadius() / (float)SimpleCloudsConstants.CLOUD_SCALE,
+ snapshot.eyeRadius() / (float)SimpleCloudsConstants.CLOUD_SCALE,
+ snapshot.edgeFade() / (float)SimpleCloudsConstants.CLOUD_SCALE,
+ snapshot.bandCount(),
+ snapshot.bandWidth() / (float)SimpleCloudsConstants.CLOUD_SCALE,
+ snapshot.spiralTightness(),
+ snapshot.rotationPhase(),
+ snapshot.rotationSpeed(),
+ snapshot.transitionStart() / (float)SimpleCloudsConstants.CLOUD_SCALE,
+ snapshot.transitionEnd() / (float)SimpleCloudsConstants.CLOUD_SCALE,
+ snapshot.cloudTypeId(),
+ snapshot.ageTicks(),
+ Mth.clamp(snapshot.normalizedIntensity(), 0.0F, 1.0F),
+ ((snapshot.id().hashCode() & 0x7fffffff) % 10000) / 10000.0F
+ ));
+ }
+ return renderables;
+ }
+
+ private record Entry(HurricaneRenderSnapshot previous, HurricaneRenderSnapshot current, long clientUpdateTick) {
+ }
+
+ public record RenderableHurricane(
+ UUID id,
+ double centerX,
+ double centerZ,
+ float anchorY,
+ float coreRadius,
+ float stormExtentRadius,
+ float eyeRadius,
+ float edgeFade,
+ int bandCount,
+ float bandWidth,
+ float spiralTightness,
+ float rotationPhase,
+ float rotationSpeed,
+ float transitionStart,
+ float transitionEnd,
+ ResourceLocation cloudTypeId,
+ int ageTicks,
+ float intensity,
+ float seed
+ ) {
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/loading/ClientForecastLoadingLifecycle.java b/src/main/java/net/Gabou/projectatmosphere/client/loading/ClientForecastLoadingLifecycle.java
index c4a31ae6..97519abf 100644
--- a/src/main/java/net/Gabou/projectatmosphere/client/loading/ClientForecastLoadingLifecycle.java
+++ b/src/main/java/net/Gabou/projectatmosphere/client/loading/ClientForecastLoadingLifecycle.java
@@ -2,6 +2,7 @@
import net.Gabou.projectatmosphere.ProjectAtmosphere;
import net.Gabou.projectatmosphere.client.ClientSyncLock;
+import net.Gabou.projectatmosphere.client.hurricane.ClientHurricaneStateCache;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientPlayerNetworkEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@@ -15,6 +16,7 @@ private ClientForecastLoadingLifecycle() {
@SubscribeEvent
public static void onClientLogin(ClientPlayerNetworkEvent.LoggingIn event) {
ClientSyncLock.clear();
+ ClientHurricaneStateCache.clear();
ClientForecastLoadingWorkQueue.reset();
if (!ForecastLoadingState.snapshot().active()) {
ForecastLoadingState.start(
@@ -30,7 +32,9 @@ public static void onClientLogin(ClientPlayerNetworkEvent.LoggingIn event) {
@SubscribeEvent
public static void onClientLogout(ClientPlayerNetworkEvent.LoggingOut event) {
ClientSyncLock.clear();
+ ClientHurricaneStateCache.clear();
ClientForecastLoadingWorkQueue.reset();
ForecastLoadingState.reset("client_logout");
}
}
+
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/render/HurricaneShaders.java b/src/main/java/net/Gabou/projectatmosphere/client/render/HurricaneShaders.java
new file mode 100644
index 00000000..e8ca3d82
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/render/HurricaneShaders.java
@@ -0,0 +1,64 @@
+package net.Gabou.projectatmosphere.client.render;
+
+import com.mojang.blaze3d.vertex.DefaultVertexFormat;
+import net.Gabou.projectatmosphere.ProjectAtmosphere;
+import net.minecraft.client.renderer.ShaderInstance;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.client.event.RegisterShadersEvent;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.fml.common.Mod;
+
+import java.io.IOException;
+
+@Mod.EventBusSubscriber(modid = ProjectAtmosphere.MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
+public final class HurricaneShaders {
+ public static final ResourceLocation BASE_TEXTURE = ResourceLocation.fromNamespaceAndPath(ProjectAtmosphere.MODID, "textures/effects/base.png");
+ public static final ResourceLocation NOISE_TEXTURE = ResourceLocation.fromNamespaceAndPath(ProjectAtmosphere.MODID, "textures/effects/noise.png");
+ public static final ResourceLocation FLOW_TEXTURE = ResourceLocation.fromNamespaceAndPath(ProjectAtmosphere.MODID, "textures/effects/flowmap.png");
+
+ private static final ResourceLocation OPAQUE_SHADER_ID = ResourceLocation.fromNamespaceAndPath(ProjectAtmosphere.MODID, "hurricane_clouds");
+ private static final ResourceLocation OPAQUE_MASK_SHADER_ID = ResourceLocation.fromNamespaceAndPath(ProjectAtmosphere.MODID, "hurricane_eye_mask");
+ private static final ResourceLocation TRANSPARENCY_SHADER_ID = ResourceLocation.fromNamespaceAndPath(ProjectAtmosphere.MODID, "hurricane_clouds_transparency");
+ private static final ResourceLocation TRANSPARENCY_MASK_SHADER_ID = ResourceLocation.fromNamespaceAndPath(ProjectAtmosphere.MODID, "hurricane_eye_mask_transparency");
+
+ private static ShaderInstance opaqueShader;
+ private static ShaderInstance opaqueMaskShader;
+ private static ShaderInstance transparencyShader;
+ private static ShaderInstance transparencyMaskShader;
+
+ private HurricaneShaders() {
+ }
+
+ @SubscribeEvent
+ public static void onRegisterShaders(RegisterShadersEvent event) throws IOException {
+ event.registerShader(new ShaderInstance(event.getResourceProvider(), OPAQUE_SHADER_ID, DefaultVertexFormat.POSITION_TEX), loaded -> opaqueShader = loaded);
+ event.registerShader(new ShaderInstance(event.getResourceProvider(), OPAQUE_MASK_SHADER_ID, DefaultVertexFormat.POSITION_TEX), loaded -> opaqueMaskShader = loaded);
+ event.registerShader(new ShaderInstance(event.getResourceProvider(), TRANSPARENCY_SHADER_ID, DefaultVertexFormat.POSITION_TEX), loaded -> transparencyShader = loaded);
+ event.registerShader(new ShaderInstance(event.getResourceProvider(), TRANSPARENCY_MASK_SHADER_ID, DefaultVertexFormat.POSITION_TEX), loaded -> transparencyMaskShader = loaded);
+ }
+
+ public static ShaderInstance getOpaqueShader() {
+ return opaqueShader;
+ }
+
+ public static ShaderInstance getOpaqueMaskShader() {
+ return opaqueMaskShader;
+ }
+
+ public static ShaderInstance getTransparencyShader() {
+ return transparencyShader;
+ }
+
+ public static ShaderInstance getTransparencyMaskShader() {
+ return transparencyMaskShader;
+ }
+
+ public static boolean isOpaqueReady() {
+ return opaqueShader != null && opaqueMaskShader != null;
+ }
+
+ public static boolean isTransparencyReady() {
+ return transparencyShader != null && transparencyMaskShader != null;
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsDhPipelineSelector.java b/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsDhPipelineSelector.java
new file mode 100644
index 00000000..29785418
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsDhPipelineSelector.java
@@ -0,0 +1,22 @@
+package net.Gabou.projectatmosphere.client.render;
+
+import dev.nonamecrackers2.simpleclouds.SimpleCloudsMod;
+import dev.nonamecrackers2.simpleclouds.client.dh.pipeline.DhSupportPipeline;
+import dev.nonamecrackers2.simpleclouds.client.event.impl.DetermineCloudRenderPipelineEvent;
+import net.Gabou.projectatmosphere.ProjectAtmosphere;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.fml.common.Mod;
+
+@Mod.EventBusSubscriber(modid = ProjectAtmosphere.MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.FORGE)
+public final class SimpleCloudsDhPipelineSelector {
+ private SimpleCloudsDhPipelineSelector() {
+ }
+
+ @SubscribeEvent
+ public static void selectDhPipeline(DetermineCloudRenderPipelineEvent event) {
+ if (SimpleCloudsMod.dhLoaded()) {
+ event.overridePipeline(DhSupportPipeline.INSTANCE);
+ }
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsHurricaneRenderer.java b/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsHurricaneRenderer.java
new file mode 100644
index 00000000..06fab59e
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsHurricaneRenderer.java
@@ -0,0 +1,581 @@
+package net.Gabou.projectatmosphere.client.render;
+
+import com.mojang.blaze3d.pipeline.RenderTarget;
+import com.mojang.blaze3d.pipeline.TextureTarget;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferBuilder;
+import com.mojang.blaze3d.vertex.DefaultVertexFormat;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.Tesselator;
+import com.mojang.blaze3d.vertex.VertexBuffer;
+import com.mojang.blaze3d.vertex.VertexFormat;
+import dev.nonamecrackers2.simpleclouds.client.framebuffer.WeightedBlendingTarget;
+import dev.nonamecrackers2.simpleclouds.client.renderer.SimpleCloudsRenderer;
+import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import net.Gabou.projectatmosphere.client.hurricane.ClientHurricaneStateCache;
+import net.Gabou.projectatmosphere.modules.hurricane.HurricaneCloudVolume;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.renderer.ShaderInstance;
+import net.minecraft.client.renderer.texture.AbstractTexture;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Matrix4f;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL14;
+import org.lwjgl.opengl.GL30;
+import org.lwjgl.opengl.GL40;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class SimpleCloudsHurricaneRenderer {
+ public static final SimpleCloudsHurricaneRenderer INSTANCE = new SimpleCloudsHurricaneRenderer();
+
+ private static final int MAX_STORMS = 4;
+ private static final float MAX_RAY_DISTANCE_CLOUD = 1100.0F;
+
+ private ClientLevel preparedLevel;
+ private long preparedGameTime = Long.MIN_VALUE;
+ private float preparedPartialTick = Float.NaN;
+ private final List preparedHurricanes = new ArrayList<>();
+ private boolean initialized;
+ private VertexBuffer fullscreenQuad;
+ private final VolumeBoxMesh volumeBox = new VolumeBoxMesh();
+ private TextureTarget opaqueScratchTarget;
+ private WeightedBlendingTarget transparencyScratchTarget;
+
+ private SimpleCloudsHurricaneRenderer() {
+ }
+
+ public void prepareFrame(ClientLevel level, float partialTick) {
+ if (this.preparedLevel == level
+ && this.preparedGameTime == level.getGameTime()
+ && Float.compare(this.preparedPartialTick, partialTick) == 0) {
+ return;
+ }
+
+ this.ensureInitialized();
+ this.preparedLevel = level;
+ this.preparedGameTime = level.getGameTime();
+ this.preparedPartialTick = partialTick;
+ this.preparedHurricanes.clear();
+
+ for (ClientHurricaneStateCache.RenderableHurricane hurricane : ClientHurricaneStateCache.getRenderableHurricanes(partialTick)) {
+ if (this.preparedHurricanes.size() >= MAX_STORMS) {
+ break;
+ }
+ this.preparedHurricanes.add(HurricaneCloudVolume.from(hurricane, partialTick));
+ }
+ }
+
+ public void renderOpaque(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB) {
+ if (this.preparedHurricanes.isEmpty() || !HurricaneShaders.isOpaqueReady()) {
+ return;
+ }
+
+ this.ensureScratchTargets(renderer);
+ StormUniforms uniforms = StormUniforms.from(this.preparedHurricanes);
+
+ this.runOpaqueEyeMaskPass(renderer, stack, projMat, uniforms, this.opaqueScratchTarget,
+ renderer.getCloudTarget().getColorTextureId(), renderer.getCloudTarget().getDepthTextureId(), false);
+ this.runOpaqueEyeMaskPass(renderer, stack, projMat, uniforms, renderer.getCloudTarget(),
+ this.opaqueScratchTarget.getColorTextureId(), this.opaqueScratchTarget.getDepthTextureId(), true);
+ this.runOpaqueVolumePass(renderer, stack, projMat, partialTick, cloudR, cloudG, cloudB);
+ }
+
+ public void renderTransparency(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB) {
+ if (this.preparedHurricanes.isEmpty() || !HurricaneShaders.isTransparencyReady()) {
+ return;
+ }
+
+ this.ensureScratchTargets(renderer);
+ StormUniforms uniforms = StormUniforms.from(this.preparedHurricanes);
+
+ this.runTransparencyMaskPass(renderer, stack, projMat, uniforms, this.transparencyScratchTarget,
+ renderer.getCloudTransparencyTarget().getColorTextureId(),
+ renderer.getCloudTransparencyTarget().getRevealageTextureId(),
+ renderer.getCloudTransparencyTarget().getDepthTextureId(),
+ false);
+ this.runTransparencyMaskPass(renderer, stack, projMat, uniforms, renderer.getCloudTransparencyTarget(),
+ this.transparencyScratchTarget.getColorTextureId(),
+ this.transparencyScratchTarget.getRevealageTextureId(),
+ this.transparencyScratchTarget.getDepthTextureId(),
+ true);
+ this.runTransparencyVolumePass(renderer, stack, projMat, partialTick, cloudR, cloudG, cloudB);
+ }
+
+ public void close() {
+ this.preparedLevel = null;
+ this.preparedGameTime = Long.MIN_VALUE;
+ this.preparedPartialTick = Float.NaN;
+ this.preparedHurricanes.clear();
+ if (this.fullscreenQuad != null) {
+ this.fullscreenQuad.close();
+ this.fullscreenQuad = null;
+ }
+ this.volumeBox.close();
+ if (this.opaqueScratchTarget != null) {
+ this.opaqueScratchTarget.destroyBuffers();
+ this.opaqueScratchTarget = null;
+ }
+ if (this.transparencyScratchTarget != null) {
+ this.transparencyScratchTarget.destroyBuffers();
+ this.transparencyScratchTarget = null;
+ }
+ this.initialized = false;
+ }
+
+ private void ensureInitialized() {
+ if (this.initialized) {
+ return;
+ }
+ BufferBuilder builder = Tesselator.getInstance().getBuilder();
+ builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX);
+ builder.vertex(-1.0F, -1.0F, 0.0F).uv(0.0F, 0.0F).endVertex();
+ builder.vertex(1.0F, -1.0F, 0.0F).uv(1.0F, 0.0F).endVertex();
+ builder.vertex(1.0F, 1.0F, 0.0F).uv(1.0F, 1.0F).endVertex();
+ builder.vertex(-1.0F, 1.0F, 0.0F).uv(0.0F, 1.0F).endVertex();
+ this.fullscreenQuad = new VertexBuffer(VertexBuffer.Usage.STATIC);
+ this.fullscreenQuad.bind();
+ this.fullscreenQuad.upload(builder.end());
+ VertexBuffer.unbind();
+ this.initialized = true;
+ }
+
+ private void ensureScratchTargets(SimpleCloudsRenderer renderer) {
+ RenderTarget cloudTarget = renderer.getCloudTarget();
+ if (this.opaqueScratchTarget == null
+ || this.opaqueScratchTarget.width != cloudTarget.width
+ || this.opaqueScratchTarget.height != cloudTarget.height) {
+ if (this.opaqueScratchTarget != null) {
+ this.opaqueScratchTarget.destroyBuffers();
+ }
+ this.opaqueScratchTarget = new TextureTarget(cloudTarget.width, cloudTarget.height, true, Minecraft.ON_OSX);
+ }
+
+ WeightedBlendingTarget transparencyTarget = renderer.getCloudTransparencyTarget();
+ if (this.transparencyScratchTarget == null
+ || this.transparencyScratchTarget.width != transparencyTarget.width
+ || this.transparencyScratchTarget.height != transparencyTarget.height) {
+ if (this.transparencyScratchTarget != null) {
+ this.transparencyScratchTarget.destroyBuffers();
+ }
+ this.transparencyScratchTarget = new WeightedBlendingTarget(
+ transparencyTarget.width,
+ transparencyTarget.height,
+ true,
+ false
+ );
+ }
+ }
+
+ private void runOpaqueEyeMaskPass(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, StormUniforms uniforms,
+ RenderTarget destination, int sourceColorTextureId, int sourceDepthTextureId,
+ boolean protectionEnabled) {
+ ShaderInstance shader = HurricaneShaders.getOpaqueMaskShader();
+ if (shader == null) {
+ return;
+ }
+
+ destination.bindWrite(false);
+ RenderSystem.disableBlend();
+ RenderSystem.disableCull();
+ RenderSystem.disableDepthTest();
+ RenderSystem.depthMask(true);
+ RenderSystem.setShader(() -> shader);
+
+ shader.setSampler("SourceColorSampler", sourceColorTextureId);
+ shader.setSampler("SourceDepthSampler", sourceDepthTextureId);
+ this.applyCommonUniforms(shader, renderer, stack, projMat);
+ this.applyStormUniforms(shader, uniforms);
+ shader.safeGetUniform("ProtectionEnabled").set(protectionEnabled ? 1 : 0);
+ shader.apply();
+
+ this.fullscreenQuad.bind();
+ this.fullscreenQuad.drawWithShader(new Matrix4f(), new Matrix4f(), shader);
+ VertexBuffer.unbind();
+ shader.clear();
+
+ RenderSystem.enableDepthTest();
+ RenderSystem.enableCull();
+ }
+
+ private void runTransparencyMaskPass(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat, StormUniforms uniforms,
+ WeightedBlendingTarget destination, int sourceAccumTextureId,
+ int sourceRevealageTextureId, int sourceDepthTextureId,
+ boolean protectionEnabled) {
+ ShaderInstance shader = HurricaneShaders.getTransparencyMaskShader();
+ if (shader == null) {
+ return;
+ }
+
+ destination.bindWrite(false);
+ RenderSystem.disableBlend();
+ RenderSystem.disableCull();
+ RenderSystem.disableDepthTest();
+ RenderSystem.depthMask(true);
+ RenderSystem.setShader(() -> shader);
+
+ shader.setSampler("SourceAccumSampler", sourceAccumTextureId);
+ shader.setSampler("SourceRevealageSampler", sourceRevealageTextureId);
+ shader.setSampler("SourceDepthSampler", sourceDepthTextureId);
+ this.applyCommonUniforms(shader, renderer, stack, projMat);
+ this.applyStormUniforms(shader, uniforms);
+ shader.safeGetUniform("ProtectionEnabled").set(protectionEnabled ? 1 : 0);
+ shader.apply();
+
+ this.fullscreenQuad.bind();
+ this.fullscreenQuad.drawWithShader(new Matrix4f(), new Matrix4f(), shader);
+ VertexBuffer.unbind();
+ shader.clear();
+
+ RenderSystem.enableDepthTest();
+ RenderSystem.enableCull();
+ }
+
+ private void runOpaqueVolumePass(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB) {
+ ShaderInstance shader = HurricaneShaders.getOpaqueShader();
+ if (shader == null) {
+ return;
+ }
+
+ Minecraft mc = Minecraft.getInstance();
+ renderer.getCloudTarget().bindWrite(false);
+ RenderSystem.enableBlend();
+ RenderSystem.defaultBlendFunc();
+ RenderSystem.enableDepthTest();
+ RenderSystem.depthMask(true);
+ RenderSystem.depthFunc(GL11.GL_LEQUAL);
+ RenderSystem.disableCull();
+ RenderSystem.setShader(() -> shader);
+
+ AbstractTexture baseTexture = mc.getTextureManager().getTexture(HurricaneShaders.BASE_TEXTURE);
+ AbstractTexture noiseTexture = mc.getTextureManager().getTexture(HurricaneShaders.NOISE_TEXTURE);
+ AbstractTexture flowTexture = mc.getTextureManager().getTexture(HurricaneShaders.FLOW_TEXTURE);
+ shader.setSampler("BaseSampler", baseTexture);
+ shader.setSampler("NoiseSampler", noiseTexture);
+ shader.setSampler("FlowSampler", flowTexture);
+ shader.setSampler("DepthSampler", renderer.getCloudTarget().getDepthTextureId());
+
+ this.applyCommonUniforms(shader, renderer, stack, projMat);
+ shader.safeGetUniform("CloudColor").set(cloudR, cloudG, cloudB, 1.0F);
+ List renderOrder = new ArrayList<>(this.preparedHurricanes);
+ Vec3 cameraPos = mc.gameRenderer.getMainCamera().getPosition();
+ renderOrder.sort((left, right) -> Double.compare(
+ right.centerWorld().distanceToSqr(cameraPos),
+ left.centerWorld().distanceToSqr(cameraPos)
+ ));
+
+ for (HurricaneCloudVolume hurricane : renderOrder) {
+ this.applySingleStormUniforms(shader, hurricane);
+ shader.safeGetUniform("VolumeMin").set(
+ (float) hurricane.boundsMinCloud().x,
+ (float) hurricane.boundsMinCloud().y,
+ (float) hurricane.boundsMinCloud().z
+ );
+ shader.safeGetUniform("VolumeMax").set(
+ (float) hurricane.boundsMaxCloud().x,
+ (float) hurricane.boundsMaxCloud().y,
+ (float) hurricane.boundsMaxCloud().z
+ );
+ shader.apply();
+ this.volumeBox.draw(shader, stack.last().pose(), projMat);
+ shader.clear();
+ }
+
+ RenderSystem.depthMask(true);
+ RenderSystem.enableDepthTest();
+ RenderSystem.disableBlend();
+ RenderSystem.enableCull();
+ }
+
+ private void runTransparencyVolumePass(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB) {
+ ShaderInstance shader = HurricaneShaders.getTransparencyShader();
+ if (shader == null) {
+ return;
+ }
+
+ Minecraft mc = Minecraft.getInstance();
+ renderer.getCloudTransparencyTarget().bindWrite(false);
+ RenderSystem.disableCull();
+ RenderSystem.disableDepthTest();
+ RenderSystem.depthMask(false);
+ RenderSystem.setShader(() -> shader);
+
+ AbstractTexture baseTexture = mc.getTextureManager().getTexture(HurricaneShaders.BASE_TEXTURE);
+ AbstractTexture noiseTexture = mc.getTextureManager().getTexture(HurricaneShaders.NOISE_TEXTURE);
+ AbstractTexture flowTexture = mc.getTextureManager().getTexture(HurricaneShaders.FLOW_TEXTURE);
+ shader.setSampler("BaseSampler", baseTexture);
+ shader.setSampler("NoiseSampler", noiseTexture);
+ shader.setSampler("FlowSampler", flowTexture);
+ shader.setSampler("DepthSampler", renderer.getCloudTarget().getDepthTextureId());
+
+ this.applyCommonUniforms(shader, renderer, stack, projMat);
+ shader.safeGetUniform("CloudColor").set(cloudR, cloudG, cloudB, 1.0F);
+
+ GL30.glEnablei(GL11.GL_BLEND, 0);
+ GL30.glEnablei(GL11.GL_BLEND, 1);
+ GL40.glBlendEquationi(0, GL14.GL_FUNC_ADD);
+ GL40.glBlendEquationi(1, GL14.GL_FUNC_ADD);
+ GL40.glBlendFunci(0, GL11.GL_ONE, GL11.GL_ONE);
+ GL40.glBlendFunci(1, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_COLOR);
+
+ List renderOrder = new ArrayList<>(this.preparedHurricanes);
+ Vec3 cameraPos = mc.gameRenderer.getMainCamera().getPosition();
+ renderOrder.sort((left, right) -> Double.compare(
+ right.centerWorld().distanceToSqr(cameraPos),
+ left.centerWorld().distanceToSqr(cameraPos)
+ ));
+
+ for (HurricaneCloudVolume hurricane : renderOrder) {
+ this.applySingleStormUniforms(shader, hurricane);
+ shader.safeGetUniform("VolumeMin").set(
+ (float) hurricane.boundsMinCloud().x,
+ (float) hurricane.boundsMinCloud().y,
+ (float) hurricane.boundsMinCloud().z
+ );
+ shader.safeGetUniform("VolumeMax").set(
+ (float) hurricane.boundsMaxCloud().x,
+ (float) hurricane.boundsMaxCloud().y,
+ (float) hurricane.boundsMaxCloud().z
+ );
+ shader.apply();
+ this.volumeBox.draw(shader, stack.last().pose(), projMat);
+ shader.clear();
+ }
+
+ GL30.glDisablei(GL11.GL_BLEND, 0);
+ GL30.glDisablei(GL11.GL_BLEND, 1);
+ GL40.glBlendFuncSeparatei(0, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
+ GL40.glBlendFuncSeparatei(1, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
+
+ RenderSystem.depthMask(true);
+ RenderSystem.enableCull();
+ RenderSystem.enableDepthTest();
+ }
+
+ private void applyCommonUniforms(ShaderInstance shader, SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat) {
+ Minecraft mc = Minecraft.getInstance();
+ shader.safeGetUniform("ModelViewMat").set(stack.last().pose());
+ shader.safeGetUniform("ProjMat").set(projMat);
+ shader.safeGetUniform("InverseProjMat").set(new Matrix4f(projMat).invert());
+ shader.safeGetUniform("InverseModelViewMat").set(new Matrix4f(stack.last().pose()).invert());
+
+ float scale = SimpleCloudsConstants.CLOUD_SCALE;
+ float cloudHeight = this.preparedLevel == null ? 0.0F : dev.nonamecrackers2.simpleclouds.common.world.CloudManager.get(this.preparedLevel).getCloudHeight();
+ Vec3 cameraPos = mc.gameRenderer.getMainCamera().getPosition();
+ shader.safeGetUniform("CameraPos").set(
+ (float) cameraPos.x / scale,
+ ((float) cameraPos.y - cloudHeight) / scale,
+ (float) cameraPos.z / scale
+ );
+ shader.safeGetUniform("AnimationTime").set((this.preparedGameTime + this.preparedPartialTick) * 0.065F);
+ shader.safeGetUniform("MaxDistance").set(MAX_RAY_DISTANCE_CLOUD);
+ shader.safeGetUniform("OutSize").set((float) mc.getWindow().getWidth(), (float) mc.getWindow().getHeight());
+ shader.safeGetUniform("FogStart").set(renderer.getFogStart());
+ shader.safeGetUniform("FogEnd").set(renderer.getFogEnd());
+ float[] fogColor = RenderSystem.getShaderFogColor();
+ shader.safeGetUniform("FogColor").set(fogColor[0], fogColor[1], fogColor[2], fogColor[3]);
+ }
+
+ private void applyStormUniforms(ShaderInstance shader, StormUniforms uniforms) {
+ shader.safeGetUniform("StormCount").set(uniforms.stormCount());
+ shader.safeGetUniform("StormPositions").set(uniforms.stormPositions());
+ shader.safeGetUniform("StormHeights").set(uniforms.stormHeights());
+ shader.safeGetUniform("EyeRadii").set(uniforms.eyeRadii());
+ shader.safeGetUniform("EyeClearRadii").set(uniforms.eyeClearRadii());
+ shader.safeGetUniform("EyeSlopes").set(uniforms.eyeSlopes());
+ shader.safeGetUniform("EyewallThicknesses").set(uniforms.eyewallThicknesses());
+ shader.safeGetUniform("CanopyRadii").set(uniforms.canopyRadii());
+ shader.safeGetUniform("ShieldRadii").set(uniforms.shieldRadii());
+ shader.safeGetUniform("CanopyBaseFactors").set(uniforms.canopyBaseFactors());
+ shader.safeGetUniform("CanopyTopFactors").set(uniforms.canopyTopFactors());
+ shader.safeGetUniform("ShieldBaseFactors").set(uniforms.shieldBaseFactors());
+ shader.safeGetUniform("ShieldTopFactors").set(uniforms.shieldTopFactors());
+ shader.safeGetUniform("BandStartRadii").set(uniforms.bandStartRadii());
+ shader.safeGetUniform("BandEndRadii").set(uniforms.bandEndRadii());
+ shader.safeGetUniform("BandWidths").set(uniforms.bandWidths());
+ shader.safeGetUniform("BandStrengths").set(uniforms.bandStrengths());
+ shader.safeGetUniform("BandCounts").set(uniforms.bandCounts());
+ shader.safeGetUniform("FringeStrengths").set(uniforms.fringeStrengths());
+ shader.safeGetUniform("StormSpins").set(uniforms.stormSpins());
+ shader.safeGetUniform("StormIntensities").set(uniforms.stormIntensities());
+ shader.safeGetUniform("StormSeeds").set(uniforms.stormSeeds());
+ }
+
+ private void applySingleStormUniforms(ShaderInstance shader, HurricaneCloudVolume hurricane) {
+ float[] stormPositions = new float[MAX_STORMS * 3];
+ float[] stormHeights = new float[MAX_STORMS];
+ float[] eyeRadii = new float[MAX_STORMS];
+ float[] eyeClearRadii = new float[MAX_STORMS];
+ float[] eyeSlopes = new float[MAX_STORMS];
+ float[] eyewallThicknesses = new float[MAX_STORMS];
+ float[] canopyRadii = new float[MAX_STORMS];
+ float[] shieldRadii = new float[MAX_STORMS];
+ float[] canopyBaseFactors = new float[MAX_STORMS];
+ float[] canopyTopFactors = new float[MAX_STORMS];
+ float[] shieldBaseFactors = new float[MAX_STORMS];
+ float[] shieldTopFactors = new float[MAX_STORMS];
+ float[] bandStartRadii = new float[MAX_STORMS];
+ float[] bandEndRadii = new float[MAX_STORMS];
+ float[] bandWidths = new float[MAX_STORMS];
+ float[] bandStrengths = new float[MAX_STORMS];
+ float[] bandCounts = new float[MAX_STORMS];
+ float[] fringeStrengths = new float[MAX_STORMS];
+ float[] stormSpins = new float[MAX_STORMS];
+ float[] stormIntensities = new float[MAX_STORMS];
+ float[] stormSeeds = new float[MAX_STORMS];
+
+ stormPositions[0] = hurricane.centerX();
+ stormPositions[1] = hurricane.baseY();
+ stormPositions[2] = hurricane.centerZ();
+ stormHeights[0] = hurricane.height();
+ eyeRadii[0] = hurricane.eyeRadius();
+ eyeClearRadii[0] = hurricane.eyeClearRadius();
+ eyeSlopes[0] = hurricane.eyeSlope();
+ eyewallThicknesses[0] = hurricane.eyewallThickness();
+ canopyRadii[0] = hurricane.canopyRadius();
+ shieldRadii[0] = hurricane.shieldRadius();
+ canopyBaseFactors[0] = hurricane.canopyBaseFactor();
+ canopyTopFactors[0] = hurricane.canopyTopFactor();
+ shieldBaseFactors[0] = hurricane.shieldBaseFactor();
+ shieldTopFactors[0] = hurricane.shieldTopFactor();
+ bandStartRadii[0] = hurricane.bandStartRadius();
+ bandEndRadii[0] = hurricane.bandEndRadius();
+ bandWidths[0] = hurricane.bandWidth();
+ bandStrengths[0] = hurricane.bandStrength();
+ bandCounts[0] = hurricane.bandCount();
+ fringeStrengths[0] = hurricane.fringeStrength();
+ stormSpins[0] = hurricane.spin();
+ stormIntensities[0] = hurricane.intensity();
+ stormSeeds[0] = hurricane.seed();
+
+ shader.safeGetUniform("StormCount").set(1);
+ shader.safeGetUniform("StormPositions").set(stormPositions);
+ shader.safeGetUniform("StormHeights").set(stormHeights);
+ shader.safeGetUniform("EyeRadii").set(eyeRadii);
+ shader.safeGetUniform("EyeClearRadii").set(eyeClearRadii);
+ shader.safeGetUniform("EyeSlopes").set(eyeSlopes);
+ shader.safeGetUniform("EyewallThicknesses").set(eyewallThicknesses);
+ shader.safeGetUniform("CanopyRadii").set(canopyRadii);
+ shader.safeGetUniform("ShieldRadii").set(shieldRadii);
+ shader.safeGetUniform("CanopyBaseFactors").set(canopyBaseFactors);
+ shader.safeGetUniform("CanopyTopFactors").set(canopyTopFactors);
+ shader.safeGetUniform("ShieldBaseFactors").set(shieldBaseFactors);
+ shader.safeGetUniform("ShieldTopFactors").set(shieldTopFactors);
+ shader.safeGetUniform("BandStartRadii").set(bandStartRadii);
+ shader.safeGetUniform("BandEndRadii").set(bandEndRadii);
+ shader.safeGetUniform("BandWidths").set(bandWidths);
+ shader.safeGetUniform("BandStrengths").set(bandStrengths);
+ shader.safeGetUniform("BandCounts").set(bandCounts);
+ shader.safeGetUniform("FringeStrengths").set(fringeStrengths);
+ shader.safeGetUniform("StormSpins").set(stormSpins);
+ shader.safeGetUniform("StormIntensities").set(stormIntensities);
+ shader.safeGetUniform("StormSeeds").set(stormSeeds);
+ }
+
+ private record StormUniforms(
+ int stormCount,
+ float[] stormPositions,
+ float[] stormHeights,
+ float[] eyeRadii,
+ float[] eyeClearRadii,
+ float[] eyeSlopes,
+ float[] eyewallThicknesses,
+ float[] canopyRadii,
+ float[] shieldRadii,
+ float[] canopyBaseFactors,
+ float[] canopyTopFactors,
+ float[] shieldBaseFactors,
+ float[] shieldTopFactors,
+ float[] bandStartRadii,
+ float[] bandEndRadii,
+ float[] bandWidths,
+ float[] bandStrengths,
+ float[] bandCounts,
+ float[] fringeStrengths,
+ float[] stormSpins,
+ float[] stormIntensities,
+ float[] stormSeeds
+ ) {
+ static StormUniforms from(List hurricanes) {
+ float[] stormPositions = new float[MAX_STORMS * 3];
+ float[] stormHeights = new float[MAX_STORMS];
+ float[] eyeRadii = new float[MAX_STORMS];
+ float[] eyeClearRadii = new float[MAX_STORMS];
+ float[] eyeSlopes = new float[MAX_STORMS];
+ float[] eyewallThicknesses = new float[MAX_STORMS];
+ float[] canopyRadii = new float[MAX_STORMS];
+ float[] shieldRadii = new float[MAX_STORMS];
+ float[] canopyBaseFactors = new float[MAX_STORMS];
+ float[] canopyTopFactors = new float[MAX_STORMS];
+ float[] shieldBaseFactors = new float[MAX_STORMS];
+ float[] shieldTopFactors = new float[MAX_STORMS];
+ float[] bandStartRadii = new float[MAX_STORMS];
+ float[] bandEndRadii = new float[MAX_STORMS];
+ float[] bandWidths = new float[MAX_STORMS];
+ float[] bandStrengths = new float[MAX_STORMS];
+ float[] bandCounts = new float[MAX_STORMS];
+ float[] fringeStrengths = new float[MAX_STORMS];
+ float[] stormSpins = new float[MAX_STORMS];
+ float[] stormIntensities = new float[MAX_STORMS];
+ float[] stormSeeds = new float[MAX_STORMS];
+
+ for (int i = 0; i < hurricanes.size(); i++) {
+ HurricaneCloudVolume hurricane = hurricanes.get(i);
+ stormPositions[i * 3] = hurricane.centerX();
+ stormPositions[i * 3 + 1] = hurricane.baseY();
+ stormPositions[i * 3 + 2] = hurricane.centerZ();
+ stormHeights[i] = hurricane.height();
+ eyeRadii[i] = hurricane.eyeRadius();
+ eyeClearRadii[i] = hurricane.eyeClearRadius();
+ eyeSlopes[i] = hurricane.eyeSlope();
+ eyewallThicknesses[i] = hurricane.eyewallThickness();
+ canopyRadii[i] = hurricane.canopyRadius();
+ shieldRadii[i] = hurricane.shieldRadius();
+ canopyBaseFactors[i] = hurricane.canopyBaseFactor();
+ canopyTopFactors[i] = hurricane.canopyTopFactor();
+ shieldBaseFactors[i] = hurricane.shieldBaseFactor();
+ shieldTopFactors[i] = hurricane.shieldTopFactor();
+ bandStartRadii[i] = hurricane.bandStartRadius();
+ bandEndRadii[i] = hurricane.bandEndRadius();
+ bandWidths[i] = hurricane.bandWidth();
+ bandStrengths[i] = hurricane.bandStrength();
+ bandCounts[i] = hurricane.bandCount();
+ fringeStrengths[i] = hurricane.fringeStrength();
+ stormSpins[i] = hurricane.spin();
+ stormIntensities[i] = hurricane.intensity();
+ stormSeeds[i] = hurricane.seed();
+ }
+
+ return new StormUniforms(
+ hurricanes.size(),
+ stormPositions,
+ stormHeights,
+ eyeRadii,
+ eyeClearRadii,
+ eyeSlopes,
+ eyewallThicknesses,
+ canopyRadii,
+ shieldRadii,
+ canopyBaseFactors,
+ canopyTopFactors,
+ shieldBaseFactors,
+ shieldTopFactors,
+ bandStartRadii,
+ bandEndRadii,
+ bandWidths,
+ bandStrengths,
+ bandCounts,
+ fringeStrengths,
+ stormSpins,
+ stormIntensities,
+ stormSeeds
+ );
+ }
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsRenderDiagnostics.java b/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsRenderDiagnostics.java
new file mode 100644
index 00000000..d9636c5a
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsRenderDiagnostics.java
@@ -0,0 +1,303 @@
+package net.Gabou.projectatmosphere.client.render;
+
+import dev.nonamecrackers2.simpleclouds.common.cloud.CloudType;
+import dev.nonamecrackers2.simpleclouds.common.noise.NoiseSettings;
+import dev.nonamecrackers2.simpleclouds.common.world.CloudManager;
+import net.minecraft.resources.ResourceLocation;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public final class SimpleCloudsRenderDiagnostics {
+ private static final Logger LOGGER = LogManager.getLogger("ProjectAtmosphere/SimpleCloudsRender");
+ private static final boolean ENABLED = Boolean.getBoolean("projectatmosphere.simpleclouds.debugRender");
+ private static final ThreadLocal CURRENT_PASS = ThreadLocal.withInitial(PassStats::new);
+ private static final AtomicBoolean PLAYER_SAMPLE_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean SHADER_LOAD_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean REGION_UPLOAD_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean CHUNK_DECISION_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean PREPARE_MESH_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean PIPELINE_STAGE_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean ALPHA_FALLBACK_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean FINALIZE_MESH_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean PASS_SUMMARY_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean DH_PIPELINE_FALLBACK_LOGGED = new AtomicBoolean();
+ private static final AtomicBoolean DH_PASS_SUMMARY_LOGGED = new AtomicBoolean();
+ private static final ThreadLocal DH_PIPELINE_ACTIVE = ThreadLocal.withInitial(() -> false);
+
+ private SimpleCloudsRenderDiagnostics() {
+ }
+
+ public static boolean isEnabled() {
+ return ENABLED;
+ }
+
+ public static void beginPass(String passName, int totalChunks, int opaqueBytes, int transparentBytes, int opaqueElements, int transparentElements, boolean canRender, boolean transparencyEnabled, Object meshStatus) {
+ if (Boolean.TRUE.equals(DH_PIPELINE_ACTIVE.get())) {
+ return;
+ }
+ setPassStats(passName, totalChunks, opaqueBytes, transparentBytes, opaqueElements, transparentElements, canRender, transparencyEnabled, meshStatus);
+ }
+
+ private static void setPassStats(String passName, int totalChunks, int opaqueBytes, int transparentBytes, int opaqueElements, int transparentElements, boolean canRender, boolean transparencyEnabled, Object meshStatus) {
+ PassStats stats = CURRENT_PASS.get();
+ stats.passName = passName;
+ stats.totalChunks = totalChunks;
+ stats.opaqueBytes = opaqueBytes;
+ stats.transparentBytes = transparentBytes;
+ stats.opaqueElements = opaqueElements;
+ stats.transparentElements = transparentElements;
+ stats.canRender = canRender;
+ stats.transparencyEnabled = transparencyEnabled;
+ stats.meshStatus = meshStatus;
+ stats.drawCalls = 0;
+ stats.totalElements = 0;
+ stats.alphaFallbacks = 0;
+ }
+
+ public static void beginDhPipelinePass(int totalChunks, int opaqueBytes, int transparentBytes, int opaqueElements, int transparentElements, boolean canRender, boolean transparencyEnabled, Object meshStatus) {
+ DH_PIPELINE_ACTIVE.set(true);
+ setPassStats(
+ "dh_after_distant_horizons_render",
+ totalChunks,
+ opaqueBytes,
+ transparentBytes,
+ opaqueElements,
+ transparentElements,
+ canRender,
+ transparencyEnabled,
+ meshStatus
+ );
+ }
+
+ public static void recordDraw(String passName, int elementCount) {
+ PassStats stats = CURRENT_PASS.get();
+ if (stats.passName == null || "unknown".equals(stats.passName)) {
+ stats.passName = passName;
+ }
+ stats.drawCalls++;
+ stats.totalElements += Math.max(0, elementCount);
+ }
+
+ public static void noteAlphaFallback(int elementCount, int ticksSinceLastGen) {
+ PassStats stats = CURRENT_PASS.get();
+ stats.alphaFallbacks++;
+ if (ENABLED || ALPHA_FALLBACK_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] alpha fallback triggered elementCount={} ticksSinceLastGen={} pass={} drawCalls={} totalElements={}",
+ elementCount,
+ ticksSinceLastGen,
+ stats.passName,
+ stats.drawCalls,
+ stats.totalElements
+ );
+ }
+ }
+
+ public static void logPlayerSample(CloudManager> manager, double playerX, double playerZ) {
+ if (manager == null) {
+ return;
+ }
+
+ var sample = manager.getCloudTypeAtPosition((float)playerX, (float)playerZ);
+ CloudType type = sample.getLeft();
+ float fade = sample.getRight();
+ float coverage = 1.0F - fade;
+ NoiseSettings noise = type != null ? type.noiseConfig() : null;
+ float[] packedNoise = noise != null ? noise.packForShader() : new float[0];
+ if (ENABLED || PLAYER_SAMPLE_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] playerSample world=({}, {}) cloudHeight={} cloudMode={} cloudCount={} selectedType={} coverage={} fade={} weather={} stormStart={} noiseStartHeight={} noiseEndHeight={} noisePacked={}",
+ fmt(playerX),
+ fmt(playerZ),
+ manager.getCloudHeight(),
+ manager.getCloudMode(),
+ manager.getClouds().size(),
+ type != null ? type.id() : "null",
+ fmt(coverage),
+ fmt(fade),
+ type != null ? type.weatherType() : "null",
+ type != null ? fmt(type.stormStart()) : "null",
+ noise != null ? noise.getStartHeight() : -1,
+ noise != null ? noise.getEndHeight() : -1,
+ Arrays.toString(packedNoise)
+ );
+ }
+ }
+
+ public static void logShaderLoad(String stage, ResourceLocation requested, ResourceLocation actual, String shaderName, int shaderId, boolean valid) {
+ if (ENABLED || SHADER_LOAD_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] shaderLoad stage={} requested={} actual={} shaderName={} shaderId={} valid={}",
+ stage,
+ requested,
+ actual,
+ shaderName,
+ shaderId,
+ valid
+ );
+ }
+ }
+
+ public static void logRegionUpload(int cloudRegions, int filteredRegions, int uploadedRegions, int cachedTypes, int shaderId, String shaderName) {
+ if (ENABLED || REGION_UPLOAD_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] regionUpload cloudRegions={} filteredRegions={} uploadedRegions={} cachedTypes={} shaderId={} shaderName={}",
+ cloudRegions,
+ filteredRegions,
+ uploadedRegions,
+ cachedTypes,
+ shaderId,
+ shaderName
+ );
+ }
+ }
+
+ public static void logChunkGenDecision(float minX, float minZ, float maxX, float maxZ, List cornerSamples, String resultDescription) {
+ if (ENABLED || CHUNK_DECISION_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] chunkGenDecision bounds=({}, {}) -> ({}, {}) samples={} result={}",
+ fmt(minX),
+ fmt(minZ),
+ fmt(maxX),
+ fmt(maxZ),
+ cornerSamples,
+ resultDescription
+ );
+ }
+ }
+
+ public static void logPrepareMeshGen(int queuedTasks, int chunkCount, int tasksPerTick, int meshGenInterval, double originX, double originY, double originZ, float meshOffsetX, float meshOffsetZ, boolean frustumCulled) {
+ if (ENABLED || PREPARE_MESH_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] prepareMeshGen queuedTasks={} chunkCount={} tasksPerTick={} meshGenInterval={} origin=({}, {}, {}) meshOffset=({}, {}) frustumCulled={}",
+ queuedTasks,
+ chunkCount,
+ tasksPerTick,
+ meshGenInterval,
+ fmt(originX),
+ fmt(originY),
+ fmt(originZ),
+ fmt(meshOffsetX),
+ fmt(meshOffsetZ),
+ frustumCulled
+ );
+ }
+ }
+
+ public static void logPipelineStage(String pipelineName, String stage, int chunkCount, int queuedTasks, int completedTasks, boolean canRender, boolean transparencyEnabled, Object meshStatus) {
+ if (ENABLED || PIPELINE_STAGE_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] pipeline stage={} pipeline={} chunks={} queuedTasks={} completedTasks={} canRender={} transparencyEnabled={} meshStatus={}",
+ stage,
+ pipelineName,
+ chunkCount,
+ queuedTasks,
+ completedTasks,
+ canRender,
+ transparencyEnabled,
+ meshStatus
+ );
+ }
+ }
+
+ public static void logDhPipelineFallback(String selectedPipeline, int chunkCount, int queuedTasks, int completedTasks, boolean canRender, boolean transparencyEnabled, Object meshStatus) {
+ if (ENABLED || DH_PIPELINE_FALLBACK_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] dhPipelineFallback selectedPipeline={} chunks={} queuedTasks={} completedTasks={} canRender={} transparencyEnabled={} meshStatus={}",
+ selectedPipeline,
+ chunkCount,
+ queuedTasks,
+ completedTasks,
+ canRender,
+ transparencyEnabled,
+ meshStatus
+ );
+ }
+ }
+
+ public static void logFinalizeMeshGen(int completedTasks, int opaqueElements, int transparentElements, int opaqueBytes, int transparentBytes, Object meshStatus) {
+ if (ENABLED || FINALIZE_MESH_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] finalizeMeshGen completedTasks={} opaqueElements={} transparentElements={} opaqueBytes={} transparentBytes={} meshStatus={}",
+ completedTasks,
+ opaqueElements,
+ transparentElements,
+ opaqueBytes,
+ transparentBytes,
+ meshStatus
+ );
+ }
+ }
+
+ public static void endPass() {
+ if (Boolean.TRUE.equals(DH_PIPELINE_ACTIVE.get())) {
+ return;
+ }
+ PassStats stats = CURRENT_PASS.get();
+ if (ENABLED || PASS_SUMMARY_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] pass={} totalChunks={} drawCalls={} totalElements={} alphaFallbacks={} opaqueBytes={} transparentBytes={} opaqueElements={} transparentElements={} canRender={} transparencyEnabled={} meshStatus={}",
+ stats.passName,
+ stats.totalChunks,
+ stats.drawCalls,
+ stats.totalElements,
+ stats.alphaFallbacks,
+ stats.opaqueBytes,
+ stats.transparentBytes,
+ stats.opaqueElements,
+ stats.transparentElements,
+ stats.canRender,
+ stats.transparencyEnabled,
+ stats.meshStatus
+ );
+ }
+ }
+
+ public static void endDhPipelinePass() {
+ PassStats stats = CURRENT_PASS.get();
+ if (ENABLED || DH_PASS_SUMMARY_LOGGED.compareAndSet(false, true)) {
+ LOGGER.info(
+ "[SimpleCloudsRender] dhPass totalChunks={} drawCalls={} totalElements={} alphaFallbacks={} opaqueBytes={} transparentBytes={} opaqueElements={} transparentElements={} canRender={} transparencyEnabled={} meshStatus={}",
+ stats.totalChunks,
+ stats.drawCalls,
+ stats.totalElements,
+ stats.alphaFallbacks,
+ stats.opaqueBytes,
+ stats.transparentBytes,
+ stats.opaqueElements,
+ stats.transparentElements,
+ stats.canRender,
+ stats.transparencyEnabled,
+ stats.meshStatus
+ );
+ }
+ DH_PIPELINE_ACTIVE.set(false);
+ }
+
+ public static boolean isDhPipelineActive() {
+ return Boolean.TRUE.equals(DH_PIPELINE_ACTIVE.get());
+ }
+
+ private static String fmt(double value) {
+ return String.format(java.util.Locale.ROOT, "%.3f", value);
+ }
+
+ private static final class PassStats {
+ private String passName = "unknown";
+ private int totalChunks;
+ private int opaqueBytes;
+ private int transparentBytes;
+ private int opaqueElements;
+ private int transparentElements;
+ private boolean canRender;
+ private boolean transparencyEnabled;
+ private Object meshStatus;
+ private int drawCalls;
+ private int totalElements;
+ private int alphaFallbacks;
+ }
+}
diff --git a/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsTornadoRenderer.java b/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsTornadoRenderer.java
new file mode 100644
index 00000000..f82a59e2
--- /dev/null
+++ b/src/main/java/net/Gabou/projectatmosphere/client/render/SimpleCloudsTornadoRenderer.java
@@ -0,0 +1,1185 @@
+package net.Gabou.projectatmosphere.client.render;
+
+import com.mojang.blaze3d.pipeline.RenderTarget;
+import com.mojang.blaze3d.pipeline.TextureTarget;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferBuilder;
+import com.mojang.blaze3d.vertex.DefaultVertexFormat;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.Tesselator;
+import com.mojang.blaze3d.vertex.VertexBuffer;
+import com.mojang.blaze3d.vertex.VertexFormat;
+import dev.nonamecrackers2.simpleclouds.client.renderer.SimpleCloudsRenderer;
+import dev.nonamecrackers2.simpleclouds.common.cloud.SimpleCloudsConstants;
+import dev.nonamecrackers2.simpleclouds.common.world.CloudManager;
+import dev.nonamecrackers2.simpleclouds.SimpleCloudsMod;
+import net.Gabou.projectatmosphere.ProjectAtmosphere;
+import net.Gabou.projectatmosphere.modules.tornado.TornadoInstance;
+import net.Gabou.projectatmosphere.modules.tornado.TornadoManager;
+import net.Gabou.projectatmosphere.modules.weather.StormLifecyclePhase;
+import net.Gabou.projectatmosphere.config.AtmoCommonConfig;
+import net.minecraft.client.Camera;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.renderer.ShaderInstance;
+import net.minecraft.client.renderer.culling.Frustum;
+import net.minecraft.world.level.levelgen.Heightmap;
+import net.minecraft.client.renderer.texture.AbstractTexture;
+import net.minecraft.util.Mth;
+import net.minecraft.world.phys.AABB;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Matrix4f;
+import org.joml.Vector4f;
+import org.lwjgl.opengl.GL;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL43;
+import org.lwjgl.system.MemoryStack;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+
+public final class SimpleCloudsTornadoRenderer {
+ public static final SimpleCloudsTornadoRenderer INSTANCE = new SimpleCloudsTornadoRenderer();
+
+ private static final int MAX_STORMS = 8;
+ private static final float CLOUD_BLEND_PAD_ABOVE_CLOUD_BASE_WORLD = 28.0F;
+ private static final float GROUND_CONTACT_PADDING_WORLD = 8.0F;
+ private static final float GROUND_VISUAL_SINK_WORLD = 1.5F;
+ private static final float MIN_VISUAL_WORLD_WIDTH = 28.0F;
+ private static final float MIN_VISUAL_WORLD_STORM_SIZE = 140.0F;
+ private static final float MIN_VISUAL_WORLD_HEIGHT = 120.0F;
+ private static final float MAX_RAY_DISTANCE_CLOUD = 420.0F;
+ private static final float WHITEOUT_STRENGTH = 1.0F;
+ private static final float WHITEOUT_THRESHOLD = 0.015F;
+ private static final float RAY_STEP_CLOUD = 0.42F;
+ private static final float WALLCLOUD_LOWER_WORLD = 15.0F;
+ private static final float FUNNEL_TOP_OFFSET_WORLD = 13.125F;
+ private static final float FUNNEL_BASE_PADDING_WORLD = 3.75F;
+ private static final float WALLCLOUD_GATE_BELOW_ORIGIN_WORLD = 8.5F;
+ private static final float TOUCHDOWN_TOP_BLEND_WORLD = 3.75F;
+ private static final float CONNECTION_BLEND_WORLD = 1.8F;
+ private static final float DIRECT_RENDER_DOWNSAMPLE_THRESHOLD = 1.01F;
+
+ private ClientLevel preparedLevel;
+ private long preparedGameTime = Long.MIN_VALUE;
+ private float preparedPartialTick = Float.NaN;
+ private final List preparedTornadoes = new ArrayList<>();
+ private final VolumeBoxMesh volumeBox = new VolumeBoxMesh();
+ private boolean initialized;
+ private VertexBuffer fullscreenQuad;
+ private TextureTarget downsampleTarget;
+ private int resolvedDebugStormIndex = -1;
+ private long lastRenderOpaqueLogGameTime = Long.MIN_VALUE;
+ private long lastDiagnosticReportGameTime = Long.MIN_VALUE;
+ private long lastShaderBindingLogGameTime = Long.MIN_VALUE;
+
+ private SimpleCloudsTornadoRenderer() {
+ }
+
+ public void prepareFrame(ClientLevel level, float partialTick) {
+ if (this.preparedLevel == level
+ && this.preparedGameTime == level.getGameTime()
+ && Float.compare(this.preparedPartialTick, partialTick) == 0) {
+ return;
+ }
+
+ this.preparedLevel = level;
+ this.preparedGameTime = level.getGameTime();
+ this.preparedPartialTick = partialTick;
+ this.preparedTornadoes.clear();
+
+ float animationTime = TornadoManager.getShaderTime() + partialTick * 0.05F;
+ for (TornadoInstance tornado : TornadoManager.getClientTornadoes()) {
+ if (this.preparedTornadoes.size() >= MAX_STORMS) {
+ break;
+ }
+ this.preparedTornadoes.add(PreparedTornado.from(level, tornado, animationTime, partialTick));
+ }
+
+ Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
+ if (camera != null && TornadoRenderDebugState.isActive()) {
+ this.resolvedDebugStormIndex = this.resolveDebugStormIndex(
+ camera.getPosition(),
+ new Vec3(camera.getLookVector())
+ );
+ } else {
+ this.resolvedDebugStormIndex = -1;
+ }
+
+ if (shouldDebugLog(level)) {
+ debug(
+ "prepareFrame complete gameTime={} tornadoes={} debugState={} resolvedDebugStorm={}",
+ level.getGameTime(),
+ this.preparedTornadoes.size(),
+ TornadoRenderDebugState.describe(),
+ this.resolvedDebugStormIndex
+ );
+ }
+ }
+
+ public void renderOpaque(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB) {
+ this.renderOpaque(renderer, stack, projMat, partialTick, cloudR, cloudG, cloudB,
+ null, renderer.getCloudTarget().getDepthTextureId(), -1, true, false);
+ }
+
+ public void renderOpaque(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB,
+ int depthTextureId, int secondaryDepthTextureId, boolean writeDepth) {
+ this.renderOpaque(renderer, stack, projMat, partialTick, cloudR, cloudG, cloudB,
+ null, depthTextureId, secondaryDepthTextureId, writeDepth, false);
+ }
+
+ public void renderOpaque(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB,
+ Frustum frustum, int depthTextureId, int secondaryDepthTextureId, boolean writeDepth) {
+ this.renderOpaque(renderer, stack, projMat, partialTick, cloudR, cloudG, cloudB,
+ frustum, depthTextureId, secondaryDepthTextureId, writeDepth, false);
+ }
+
+ public void renderOpaque(SimpleCloudsRenderer renderer, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB,
+ Frustum frustum, int depthTextureId, int secondaryDepthTextureId,
+ boolean writeDepth, boolean distantHorizonsDepthMode) {
+ this.renderOpaqueToTarget(renderer, null, stack, projMat, partialTick, cloudR, cloudG, cloudB,
+ frustum, depthTextureId, secondaryDepthTextureId, writeDepth, distantHorizonsDepthMode);
+ }
+
+ public void renderOpaqueToTarget(SimpleCloudsRenderer renderer, RenderTarget target, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB,
+ Frustum frustum, int depthTextureId, int secondaryDepthTextureId,
+ boolean writeDepth, boolean distantHorizonsDepthMode) {
+ this.renderOpaqueToTarget(renderer, target, stack, projMat, partialTick, cloudR, cloudG, cloudB,
+ frustum, depthTextureId, secondaryDepthTextureId, writeDepth, distantHorizonsDepthMode, false);
+ }
+
+ public void renderOpaqueToTarget(SimpleCloudsRenderer renderer, RenderTarget target, PoseStack stack, Matrix4f projMat,
+ float partialTick, float cloudR, float cloudG, float cloudB,
+ Frustum frustum, int depthTextureId, int secondaryDepthTextureId,
+ boolean writeDepth, boolean distantHorizonsDepthMode,
+ boolean forceDisableFramebufferDepth) {
+ ClientLevel level = Minecraft.getInstance().level;
+ boolean pathLog = shouldPathLog(level);
+ if (pathLog && this.lastRenderOpaqueLogGameTime != level.getGameTime()) {
+ this.lastRenderOpaqueLogGameTime = level.getGameTime();
+ path(
+ "renderOpaque called gameTime={} tornadoes={} shaderReady={} debugState={} resolvedDebugStorm={} targetOverride={} dhMode={} depth={} secondaryDepth={} writeDepth={} forceNoFbDepth={}",
+ level.getGameTime(),
+ this.preparedTornadoes.size(),
+ TornadoShaders.isReady(),
+ TornadoRenderDebugState.describe(),
+ this.resolvedDebugStormIndex,
+ target != null,
+ distantHorizonsDepthMode,
+ depthTextureId,
+ secondaryDepthTextureId,
+ writeDepth,
+ forceDisableFramebufferDepth
+ );
+ }
+ if (this.preparedTornadoes.isEmpty()) {
+ if (pathLog) {
+ path("renderOpaque skipped: no prepared tornadoes");
+ }
+ return;
+ }
+ if (!TornadoShaders.isReady()) {
+ if (pathLog) {
+ path("renderOpaque skipped: tornado shaders are not ready");
+ }
+ return;
+ }
+
+ Minecraft mc = Minecraft.getInstance();
+ ShaderInstance shader = TornadoShaders.getShader();
+ if (shader == null) {
+ if (pathLog) {
+ path("renderOpaque skipped: tornado shader instance is null");
+ }
+ return;
+ }
+
+ TornadoRenderDebugState.Mode debugMode = TornadoRenderDebugState.isActive()
+ ? TornadoRenderDebugState.getMode()
+ : TornadoRenderDebugState.Mode.OFF;
+ TornadoRenderDebugState.Mode shaderMode = !distantHorizonsDepthMode && debugMode == TornadoRenderDebugState.Mode.OFF
+ ? TornadoRenderDebugState.Mode.FULL
+ : debugMode;
+ float downsample = getConfiguredDownsample();
+ boolean useTargetOverride = target != null;
+ RenderTarget destinationTarget = useTargetOverride ? target : renderer.getCloudTarget();
+ boolean useDownsample = this.canUseDownsamplePath()
+ && (!useTargetOverride || distantHorizonsDepthMode);
+ RenderTarget renderTarget = useDownsample ? this.prepareDownsampleTarget(destinationTarget, downsample) : destinationTarget;
+ if (renderTarget == null) {
+ if (pathLog) {
+ path("renderOpaque skipped: render target is null useDownsample={} targetOverride={}", useDownsample, useTargetOverride);
+ }
+ return;
+ }
+ boolean disableFramebufferDepth = debugMode == TornadoRenderDebugState.Mode.DEPTH_NO_FRAMEBUFFER
+ || debugMode == TornadoRenderDebugState.Mode.OCCLUSION
+ || debugMode == TornadoRenderDebugState.Mode.LATE
+ || forceDisableFramebufferDepth
+ || useDownsample;
+ boolean deferSceneDepthReject = useDownsample && !distantHorizonsDepthMode;
+ boolean writeDownsampleDepth = deferSceneDepthReject;
+ if (pathLog) {
+ path(
+ "renderOpaque state debugMode={} shaderMode={} configuredDownsample={} canDownsample={} useDownsample={} targetOverride={} destination={}x{} renderTarget={}x{} disableFramebufferDepth={} deferSceneDepthReject={} writeDownsampleDepth={}",
+ debugMode,
+ shaderMode,
+ downsample,
+ this.canUseDownsamplePath(),
+ useDownsample,
+ useTargetOverride,
+ destinationTarget.width,
+ destinationTarget.height,
+ renderTarget.width,
+ renderTarget.height,
+ disableFramebufferDepth,
+ deferSceneDepthReject,
+ writeDownsampleDepth
+ );
+ }
+
+ this.pushGpuDebugGroup("ProjectAtmosphere Tornado Opaque");
+ try {
+ if (useDownsample) {
+ this.clearDownsampleTarget();
+ }
+
+ renderTarget.bindWrite(useDownsample);
+ if (useDownsample && !distantHorizonsDepthMode) {
+ RenderSystem.disableBlend();
+ } else {
+ RenderSystem.enableBlend();
+ RenderSystem.defaultBlendFunc();
+ }
+ if (writeDownsampleDepth) {
+ RenderSystem.enableDepthTest();
+ RenderSystem.depthMask(true);
+ } else if (disableFramebufferDepth) {
+ RenderSystem.disableDepthTest();
+ RenderSystem.depthMask(false);
+ } else {
+ RenderSystem.enableDepthTest();
+ RenderSystem.depthMask(writeDepth);
+ }
+ RenderSystem.depthFunc(writeDownsampleDepth ? GL11.GL_ALWAYS : GL11.GL_LEQUAL);
+ RenderSystem.setShader(() -> shader);
+
+ AbstractTexture tornadoTexture = mc.getTextureManager().getTexture(TornadoShaders.TORNADO_TEXTURE);
+ AbstractTexture noiseTexture = mc.getTextureManager().getTexture(TornadoShaders.NOISE_TEXTURE);
+ AbstractTexture flowTexture = mc.getTextureManager().getTexture(TornadoShaders.FLOW_TEXTURE);
+ shader.setSampler("TornadoSampler", tornadoTexture);
+ shader.setSampler("NoiseSampler", noiseTexture);
+ shader.setSampler("FlowSampler", flowTexture);
+ shader.setSampler("DepthSampler", depthTextureId);
+ shader.setSampler("SecondaryDepthSampler", secondaryDepthTextureId > 0 ? secondaryDepthTextureId : depthTextureId);
+
+ shader.safeGetUniform("ModelViewMat").set(stack.last().pose());
+ shader.safeGetUniform("ProjMat").set(projMat);
+ Matrix4f inverseProj = new Matrix4f(projMat).invert();
+ Matrix4f inverseModelView = new Matrix4f(stack.last().pose()).invert();
+ shader.safeGetUniform("InverseProjMat").set(inverseProj);
+ shader.safeGetUniform("InverseModelViewMat").set(inverseModelView);
+
+ float scale = SimpleCloudsConstants.CLOUD_SCALE;
+ float cloudHeight = CloudManager.get(level).getCloudHeight();
+ Vec3 cameraPos = mc.gameRenderer.getMainCamera().getPosition();
+ Vec3 cameraPosCloud = new Vec3(
+ cameraPos.x / scale,
+ (cameraPos.y - cloudHeight) / scale,
+ cameraPos.z / scale
+ );
+ shader.safeGetUniform("CameraPos").set((float) cameraPosCloud.x, (float) cameraPosCloud.y, (float) cameraPosCloud.z);
+
+ shader.safeGetUniform("CloudScale").set(scale);
+ shader.safeGetUniform("RenderQuality").set((float) AtmoCommonConfig.TORNADO_RENDER_QUALITY.get().doubleValue());
+ shader.safeGetUniform("UseSecondaryDepthSampler").set(secondaryDepthTextureId > 0 && secondaryDepthTextureId != depthTextureId ? 1 : 0);
+ shader.safeGetUniform("DistantHorizonsDepthMode").set(distantHorizonsDepthMode ? 1 : 0);
+ shader.safeGetUniform("DeferSceneDepthReject").set(deferSceneDepthReject ? 1 : 0);
+
+ shader.safeGetUniform("CloudColor").set(cloudR, cloudG, cloudB, 1.0F);
+ shader.safeGetUniform("AnimationTime").set(TornadoManager.getShaderTime() + partialTick * 0.05F);
+ shader.safeGetUniform("MaxDistance").set(MAX_RAY_DISTANCE_CLOUD);
+ shader.safeGetUniform("OutSize").set((float) renderTarget.width, (float) renderTarget.height);
+ shader.safeGetUniform("FogStart").set(renderer.getFogStart());
+ shader.safeGetUniform("FogEnd").set(renderer.getFogEnd());
+ float[] fogColor = RenderSystem.getShaderFogColor();
+ shader.safeGetUniform("FogColor").set(fogColor[0], fogColor[1], fogColor[2], fogColor[3]);
+
+ this.maybeLogShaderBindings(level, shader, depthTextureId, secondaryDepthTextureId);
+ this.maybeEmitDiagnosticReport(level, inverseProj, inverseModelView, stack.last().pose(), projMat, cameraPos, cameraPosCloud, writeDepth);
+
+
+ List