diff --git a/game/client/build.gradle.kts b/game/client/build.gradle.kts
index 585899a05..2a8647a40 100644
--- a/game/client/build.gradle.kts
+++ b/game/client/build.gradle.kts
@@ -125,6 +125,8 @@ val run by tasks.registering(JavaExec::class) {
     }
 
     systemProperty("java.library.path", layout.buildDirectory.dir("natives").get().asFile.absolutePath)
+
+    println(allJvmArgs);
 }
 
 val buildJar by tasks.registering(ShadowJar::class) {
diff --git a/game/client/src/main/java/net/minecraft/client/Minecraft.java b/game/client/src/main/java/net/minecraft/client/Minecraft.java
index 0496247ab..3813b09d7 100644
--- a/game/client/src/main/java/net/minecraft/client/Minecraft.java
+++ b/game/client/src/main/java/net/minecraft/client/Minecraft.java
@@ -1168,13 +1168,18 @@ public class Minecraft
                         LOGGER.error("Exception occurred while taking panoramic screenshot!", e);
                     }
                 } else {
-                    this.hudIngame.addChatMessage(ScreenShot.saveScreenshot(getMinecraftDir(), this.gameWindow.getWidthPixels(), this.gameWindow.getHeightPixels()));
+                    ScreenShot.saveScreenshot(this, getMinecraftDir(), this.gameWindow.getWidthPixels(), this.gameWindow.getHeightPixels());
                 }
                 Debug.pop();
             }
         } else {
             this.isTakingScreenshot = false;
         }
+
+        final @Nullable String message = ScreenShot.tickScreenShot();
+        if (message != null) {
+            this.hudIngame.addChatMessage(message);
+        }
     }
 
     public void shutdown() {
@@ -2604,29 +2609,13 @@ public class Minecraft
         return SoundRepository.allKeys;
     }
 
-    public boolean isAmbientOcclusionEnabled() {
-        return GameSettings.AMBIENT_OCCLUSION.value;
-    }
-
-    public boolean isTreeShadingEnabled() {
-        return GameSettings.TREE_SHADOWS.value;
-    }
-
-    public boolean isDebugInfoEnabled() {
-        return GameSettings.SHOW_DEBUG_SCREEN.value;
-    }
-
     @Override
     public boolean isFullbrightEnabled() {
         return this.fullbright;
     }
 
     public boolean canRenderEntityLabel() {
-        return isDebugInfoEnabled() && this.thePlayer != null && this.thePlayer.getGamemode() == Gamemodes.CREATIVE;
-    }
-
-    public float getOutlineWidth() {
-        return GameSettings.BLOCK_OUTLINE_WIDTH.value;
+        return GameSettings.SHOW_DEBUG_SCREEN.value && this.thePlayer != null && this.thePlayer.getGamemode() == Gamemodes.CREATIVE;
     }
 
     public int getAutosaveTimer() {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenPhotoMode.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenPhotoMode.java
index 7018b3aa5..0505bb983 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenPhotoMode.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenPhotoMode.java
@@ -338,7 +338,7 @@ public class ScreenPhotoMode extends Screen {
         if (!this.shouldScreenshot) {
             super.render(mx, my, partialTick);
         } else {
-            ScreenShot.saveScreenshot(Global.accessor.getMinecraftDir(), this.mc.gameWindow.getWidthPixels(), this.mc.gameWindow.getHeightPixels());
+            ScreenShot.saveScreenshot(mc, Global.accessor.getMinecraftDir(), this.mc.gameWindow.getWidthPixels(), this.mc.gameWindow.getHeightPixels());
             this.shouldScreenshot = false;
         }
         scroll(Mouse.getDWheel());
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngame.java b/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngame.java
index dffb4cdec..8ca37f5d6 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngame.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngame.java
@@ -309,7 +309,7 @@ public class HudIngame extends Gui {
                     int blockZ = MathHelper.floor(this.mc.thePlayer.z);
 
                     drawDebugScreenLine("Season: " + seasonString);
-                    drawDebugScreenLine("Weather: " + weatherString + " (" + (this.mc.currentWorld.weatherManager.getWeatherDuration() / 20) + "s I: " + percentRound(this.mc.currentWorld.weatherManager.getWeatherIntensity()) + "% P: " + percentRound(this.mc.currentWorld.weatherManager.getWeatherPower()) + "%)");
+                    drawDebugScreenLine("Weather: " + weatherString + " (" + (this.mc.currentWorld.getWeatherManager().getWeatherDuration() / 20) + "s I: " + percentRound(this.mc.currentWorld.getWeatherManager().getWeatherIntensity()) + "% P: " + percentRound(this.mc.currentWorld.getWeatherManager().getWeatherPower()) + "%)");
                     drawDebugScreenLine("Biome: " + Registries.BIOMES.getKey(this.mc.currentWorld.getBlockBiome(blockX, blockY, blockZ)));
 
                     float temperature = (float) floor100(this.mc.currentWorld.getBlockTemperature(blockX, blockZ));
diff --git a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/ScreenModelViewer.java b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/ScreenModelViewer.java
index 273614ceb..8b83cc91c 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/ScreenModelViewer.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/ScreenModelViewer.java
@@ -18,7 +18,7 @@ import net.minecraft.client.gui.options.data.OptionsPages;
 import net.minecraft.client.render.renderer.GLRenderer;
 import net.minecraft.client.render.shader.PhotoModeRenderer;
 import net.minecraft.client.render.shader.ShadersRenderer;
-import net.minecraft.client.render.texture.Texture;
+import net.minecraft.client.render.shader.framebuffer.FrameBuffer;
 import net.minecraft.client.util.helper.FileOpener;
 import net.minecraft.client.util.helper.Textures;
 import net.minecraft.core.block.Blocks;
@@ -37,10 +37,7 @@ import java.io.File;
 import java.nio.ByteBuffer;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
+import java.util.*;
 
 import static org.lwjgl.opengl.GL41.GL_RGBA;
 import static org.lwjgl.opengl.GL41.GL_TEXTURE_2D;
@@ -470,8 +467,8 @@ public class ScreenModelViewer extends ScreenPhotoMode implements ListenerTextFi
             File screenshotsDir = new File(this.mc.getMinecraftDir(), "screenshots");
             //noinspection ResultOfMethodCallIgnored
             screenshotsDir.mkdir();
-            Texture texture = ((ShadersRenderer) this.mc.renderer).getWorldFramebufferColor();
-            texture.bind();
+            final @NotNull FrameBuffer frameBuffer = Objects.requireNonNull(((ShadersRenderer) this.mc.renderer).getWorldFrameBuffer());
+            Objects.requireNonNull(frameBuffer.getAttachment(FrameBuffer.AttachmentType.COLOR)).bindForOutput();
 
             int w = glGetTexLevelParameteri(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH);
             int h = glGetTexLevelParameteri(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT);
@@ -502,8 +499,8 @@ public class ScreenModelViewer extends ScreenPhotoMode implements ListenerTextFi
 
     public void specificScreenshot(@NotNull File destination) {
         try {
-            Texture texture = ((ShadersRenderer) this.mc.renderer).getWorldFramebufferColor();
-            texture.bind();
+            final @NotNull FrameBuffer frameBuffer = Objects.requireNonNull(((ShadersRenderer) this.mc.renderer).getWorldFrameBuffer());
+            Objects.requireNonNull(frameBuffer.getAttachment(FrameBuffer.AttachmentType.COLOR)).bindForOutput();
 
             int w = glGetTexLevelParameteri(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH);
             int h = glGetTexLevelParameteri(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT);
diff --git a/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java b/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java
index 8e708c319..4e53b667b 100644
--- a/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java
+++ b/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java
@@ -244,7 +244,7 @@ public class PacketHandlerClient extends PacketHandler {
         if (this.worldClientMP.dimension.id != packet.dimId) return;
         Weather currentWeather = packet.id == -1 ? null : Weathers.WEATHERS[packet.id];
         Weather newWeather = packet.newId == -1 ? null : Weathers.WEATHERS[packet.newId];
-        this.worldClientMP.weatherManager.overrideWeather(currentWeather, newWeather, packet.duration, packet.intensity, packet.power);
+        this.worldClientMP.getWeatherManager().overrideWeather(currentWeather, newWeather, packet.duration, packet.intensity, packet.power);
     }
 
     @Override
diff --git a/game/client/src/main/java/net/minecraft/client/option/GameSettings.java b/game/client/src/main/java/net/minecraft/client/option/GameSettings.java
index 652a073fa..f4e5af88c 100644
--- a/game/client/src/main/java/net/minecraft/client/option/GameSettings.java
+++ b/game/client/src/main/java/net/minecraft/client/option/GameSettings.java
@@ -11,16 +11,7 @@ import net.minecraft.client.gui.TooltipElement;
 import net.minecraft.client.gui.hud.component.HudComponents;
 import net.minecraft.client.gui.options.ScreenOptions;
 import net.minecraft.client.input.InputDevice;
-import net.minecraft.client.option.enums.CloudQuality;
-import net.minecraft.client.option.enums.Colorblindness;
-import net.minecraft.client.option.enums.DescriptionPromptEnum;
-import net.minecraft.client.option.enums.LeavesQuality;
-import net.minecraft.client.option.enums.LightmapStyle;
-import net.minecraft.client.option.enums.MipmapType;
-import net.minecraft.client.option.enums.ParticlesQuality;
-import net.minecraft.client.option.enums.RenderScale;
-import net.minecraft.client.option.enums.TextOutlineQuality;
-import net.minecraft.client.option.enums.TextureAnimation;
+import net.minecraft.client.option.enums.*;
 import net.minecraft.client.render.font.Fonts;
 import net.minecraft.client.world.chunk.provider.SelectedProvider;
 import net.minecraft.core.entity.player.Player;
diff --git a/game/client/src/main/java/net/minecraft/client/option/enums/MSAA.java b/game/client/src/main/java/net/minecraft/client/option/enums/MSAA.java
new file mode 100644
index 000000000..f741339fd
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/option/enums/MSAA.java
@@ -0,0 +1,14 @@
+package net.minecraft.client.option.enums;
+
+public enum MSAA {
+    OFF(0),
+    X2(2),
+    X4(4),
+    X8(8);
+
+    public final int samples;
+
+    MSAA(final int samples) {
+        this.samples = samples;
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/FogManager.java b/game/client/src/main/java/net/minecraft/client/render/FogManager.java
index 717428414..0caf8c889 100644
--- a/game/client/src/main/java/net/minecraft/client/render/FogManager.java
+++ b/game/client/src/main/java/net/minecraft/client/render/FogManager.java
@@ -40,7 +40,7 @@ public class FogManager {
         this.fogBrightnessOld = this.fogBrightness;
 
         float fogModifier = this.mc.currentWorld.getCurrentWeather() != null ? this.mc.currentWorld.getCurrentWeather().getFogDistanceMultiplier() : 1.0f;
-        fogModifier = (1.0f - ((1.0f - fogModifier) * this.mc.currentWorld.weatherManager.getWeatherIntensity() * this.mc.currentWorld.weatherManager.getWeatherPower()));
+        fogModifier = (1.0f - ((1.0f - fogModifier) * this.mc.currentWorld.getWeatherManager().getWeatherIntensity() * this.mc.currentWorld.getWeatherManager().getWeatherPower()));
 
         float brightnessAtCamera = this.mc.currentWorld.getLightBrightness(this.mc.activeCamera.getTilePos());
         if (this.mc.fullbright) brightnessAtCamera = 1.0f;
@@ -83,7 +83,7 @@ public class FogManager {
                 float maxFogDistance = GameSettings.RENDER_DISTANCE.highest * 16;
 
                 float fogModifier = this.mc.currentWorld.getCurrentWeather() != null ? this.mc.currentWorld.getCurrentWeather().getFogDistanceMultiplier() : 1.0f;
-                fogModifier = (1.0f - ((1.0f - fogModifier) * this.mc.currentWorld.weatherManager.getWeatherIntensity() * this.mc.currentWorld.weatherManager.getWeatherPower()));
+                fogModifier = (1.0f - ((1.0f - fogModifier) * this.mc.currentWorld.getWeatherManager().getWeatherIntensity() * this.mc.currentWorld.getWeatherManager().getWeatherPower()));
 
                 if (fogDistance > maxFogDistance * fogModifier) fogDistance = maxFogDistance * fogModifier;
 
@@ -128,7 +128,7 @@ public class FogManager {
         this.fogBlue += (bSky - this.fogBlue) * renderDistanceScale;
 
         if (world.getCurrentWeather() != null) {
-            float[] out = world.getCurrentWeather().modifyFogColor(this.fogRed, this.fogGreen, this.fogBlue, world.weatherManager.getWeatherIntensity() * world.weatherManager.getWeatherPower());
+            float[] out = world.getCurrentWeather().modifyFogColor(this.fogRed, this.fogGreen, this.fogBlue, world.getWeatherManager().getWeatherIntensity() * world.getWeatherManager().getWeatherPower());
             this.fogRed = out[0];
             this.fogGreen = out[1];
             this.fogBlue = out[2];
diff --git a/game/client/src/main/java/net/minecraft/client/render/FrameBuffer.java b/game/client/src/main/java/net/minecraft/client/render/FrameBuffer.java
index bdfdae08f..7293db94b 100644
--- a/game/client/src/main/java/net/minecraft/client/render/FrameBuffer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/FrameBuffer.java
@@ -4,7 +4,7 @@ import static org.lwjgl.opengl.ARBFramebufferObject.*;
 
 public class FrameBuffer {
 	
-	private int id;
+	protected int id;
 	
 	public boolean generate() {
 		if(isGenerated()) {
@@ -26,6 +26,7 @@ public class FrameBuffer {
 		
 		glDeleteFramebuffers(id);
 		id = 0;
+
 		return true;
 	}
 	
diff --git a/game/client/src/main/java/net/minecraft/client/render/FrameBufferMultisampled.java b/game/client/src/main/java/net/minecraft/client/render/FrameBufferMultisampled.java
new file mode 100644
index 000000000..0274ec8ae
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/FrameBufferMultisampled.java
@@ -0,0 +1,63 @@
+package net.minecraft.client.render;
+
+import org.lwjgl.opengl.GL41;
+
+public class FrameBufferMultisampled extends FrameBuffer {
+    private int blitId;
+
+    @Override
+    public boolean generate() {
+        if (!super.generate()) {
+            return false;
+        }
+
+        this.blitId = GL41.glGenFramebuffers();
+        if(!this.isGenerated()) {
+            throw new RuntimeException("Framebuffer not created!");
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean delete() {
+        if (!super.delete()) {
+            return false;
+        }
+
+        GL41.glDeleteFramebuffers(this.blitId);
+        this.blitId = 0;
+
+        return true;
+    }
+
+    public boolean blit(final int width, final int height) {
+        if (!this.isGenerated()) {
+            return false;
+        }
+
+        GL41.glBindFramebuffer(GL41.GL_READ_FRAMEBUFFER, this.id);
+        GL41.glBindFramebuffer(GL41.GL_DRAW_FRAMEBUFFER, this.blitId);
+        GL41.glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL41.GL_COLOR_BUFFER_BIT, GL41.GL_NEAREST);
+
+        GL41.glBindFramebuffer(GL41.GL_READ_FRAMEBUFFER, 0);
+        GL41.glBindFramebuffer(GL41.GL_DRAW_FRAMEBUFFER, 0);
+
+        return true;
+    }
+
+    public boolean bindBlit() {
+        if (!this.isGenerated()) {
+            return false;
+        }
+
+        GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, this.blitId);
+
+        return true;
+    }
+
+    @Override
+    public boolean isGenerated() {
+        return super.isGenerated() && this.blitId != 0;
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/LightingCache.java b/game/client/src/main/java/net/minecraft/client/render/LightingCache.java
index 3f7e15b42..758fdc02f 100644
--- a/game/client/src/main/java/net/minecraft/client/render/LightingCache.java
+++ b/game/client/src/main/java/net/minecraft/client/render/LightingCache.java
@@ -1,6 +1,7 @@
 package net.minecraft.client.render;
 
 import net.minecraft.client.Minecraft;
+import net.minecraft.client.option.GameSettings;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.world.WorldSource;
@@ -46,7 +47,7 @@ final public class LightingCache {
     }
 
     private float calcBrightness(final @NotNull TilePos tilePos) {
-        if (this.block.emission >= 15 || !this.mc.isAmbientOcclusionEnabled()) {
+        if (this.block.emission >= 15 || !GameSettings.AMBIENT_OCCLUSION.value) {
             return 1.0f;
         }
         return 1.0f - this.access.getBlockType(tilePos).getAmbientOcclusionStrength(this.access, tilePos);
diff --git a/game/client/src/main/java/net/minecraft/client/render/PostProcessingManager.java b/game/client/src/main/java/net/minecraft/client/render/PostProcessingManager.java
index 5ba74f788..ecae3acf0 100644
--- a/game/client/src/main/java/net/minecraft/client/render/PostProcessingManager.java
+++ b/game/client/src/main/java/net/minecraft/client/render/PostProcessingManager.java
@@ -128,7 +128,7 @@ public class PostProcessingManager {
         // Perform weather effects
         final Weather activeWeather = this.mc.currentWorld.getCurrentWeather();
         if (activeWeather != null) {
-            final float intensity = this.mc.currentWorld.weatherManager.getWeatherIntensity() * this.mc.currentWorld.weatherManager.getWeatherIntensity();
+            final float intensity = this.mc.currentWorld.getWeatherManager().getWeatherIntensity() * this.mc.currentWorld.getWeatherManager().getWeatherIntensity();
             if (activeWeather == Weathers.OVERWORLD_SNOW || activeWeather == Weathers.OVERWORLD_WINTER_SNOW) {
                 this.current.contrast += -0.125f * intensity * surfaceness;
                 this.current.saturation += -0.25f * intensity * surfaceness;
diff --git a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
index 36e126c8b..d7046525b 100644
--- a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
+++ b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
@@ -745,7 +745,7 @@ public class RenderGlobal
         GLRenderer.setDepthMask(false);
         GLRenderer.enableState(State.BLEND);
         GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE);
-        float sunAlpha = 1.0F - (this.world.getCurrentWeather() != null && this.world.getCurrentWeather() != Weathers.OVERWORLD_CLEAR ? (this.world.weatherManager.getWeatherIntensity() * this.world.weatherManager.getWeatherPower()) * 1.5f : 0.0f);
+        float sunAlpha = 1.0F - (this.world.getCurrentWeather() != null && this.world.getCurrentWeather() != Weathers.OVERWORLD_CLEAR ? (this.world.getWeatherManager().getWeatherIntensity() * this.world.getWeatherManager().getWeatherPower()) * 1.5f : 0.0f);
 
         float x = 0.0F;
         float y = 0.0F;
@@ -792,7 +792,7 @@ public class RenderGlobal
             Weather[] blocked = this.world.getBlockBiome(new TilePos(this.mc.thePlayer)).blockedWeathers;
             boolean isWeatherBlocked = false;
             for (Weather w : blocked) {
-                if (w.equals(this.world.weatherManager.getLastWeather())) {
+                if (w.equals(this.world.getWeatherManager().getLastWeather())) {
                     isWeatherBlocked = true;
                     break;
                 }
@@ -1190,7 +1190,7 @@ public class RenderGlobal
             GLRenderer.pushFrame();
             GLRenderer.enableState(State.BLEND);
             GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
-            float w = this.mc.getOutlineWidth();
+            float w = GameSettings.BLOCK_OUTLINE_WIDTH.get();
             GLRenderer.setColor4f(0.0F, 0.0F, 0.0F, 0.35f + w * 0.3f);
             GLRenderer.setLineWidth(org.joml.Math.max(4.0f * w * 2.0f, 1.0f));
             GLRenderer.setShader(Shaders.LINES);
diff --git a/game/client/src/main/java/net/minecraft/client/render/WorldRenderer.java b/game/client/src/main/java/net/minecraft/client/render/WorldRenderer.java
index 96bf0b895..61f3bbb40 100644
--- a/game/client/src/main/java/net/minecraft/client/render/WorldRenderer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/WorldRenderer.java
@@ -735,7 +735,7 @@ public class WorldRenderer {
         Weather weather = this.mc.currentWorld.getCurrentWeather();
         if (!(weather instanceof IPrecipitation precipitation) || !precipitation.spawnRainParticles()) return;
 
-        float f = this.mc.currentWorld.weatherManager.getWeatherIntensity() * this.mc.currentWorld.weatherManager.getWeatherPower();
+        float f = this.mc.currentWorld.getWeatherManager().getWeatherIntensity() * this.mc.currentWorld.getWeatherManager().getWeatherPower();
 
         if (GameSettings.PARTICLES_QUALITY.value == ParticlesQuality.REDUCED) {
             f /= 2.0F;
@@ -788,9 +788,9 @@ public class WorldRenderer {
         if (l > 0 && this.random.nextInt(3) < this.rainSoundCounter++) {
             this.rainSoundCounter = 0;
             if (d1 > this.mc.activeCamera.getY() + 1.0D && world.getHeightValue(MathHelper.floor(this.mc.activeCamera.getX()), MathHelper.floor(this.mc.activeCamera.getZ())) > MathHelper.floor(this.mc.activeCamera.getY())) {
-                this.mc.currentWorld.playSoundEffect(null, SoundCategory.WEATHER_SOUNDS, d, d1, d2, "ambient.weather.rain", 0.1F * this.mc.currentWorld.weatherManager.getWeatherIntensity() * this.mc.currentWorld.weatherManager.getWeatherPower() * 0.5f, 0.5F);
+                this.mc.currentWorld.playSoundEffect(null, SoundCategory.WEATHER_SOUNDS, d, d1, d2, "ambient.weather.rain", 0.1F * this.mc.currentWorld.getWeatherManager().getWeatherIntensity() * this.mc.currentWorld.getWeatherManager().getWeatherPower() * 0.5f, 0.5F);
             } else {
-                this.mc.currentWorld.playSoundEffect(null, SoundCategory.WEATHER_SOUNDS, d, d1, d2, "ambient.weather.rain", 0.2F * this.mc.currentWorld.weatherManager.getWeatherIntensity() * this.mc.currentWorld.weatherManager.getWeatherPower() * 0.5f, 1.0F);
+                this.mc.currentWorld.playSoundEffect(null, SoundCategory.WEATHER_SOUNDS, d, d1, d2, "ambient.weather.rain", 0.2F * this.mc.currentWorld.getWeatherManager().getWeatherIntensity() * this.mc.currentWorld.getWeatherManager().getWeatherPower() * 0.5f, 1.0F);
             }
         }
     }
@@ -813,7 +813,7 @@ public class WorldRenderer {
         GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
 //        glAlphaFunc(516, 0.01F);
 
-        this.mc.textureManager.loadTexture(precipitation.getPrecipitationTexturePath(world.weatherManager.getWeatherPower(), world.weatherManager.getWeatherIntensity())).bind();
+        this.mc.textureManager.loadTexture(precipitation.getPrecipitationTexturePath(world.getWeatherManager().getWeatherPower(), world.getWeatherManager().getWeatherIntensity())).bind();
 
         double nextPosX = this.mc.activeCamera.getX(partialTick);
         double nextPosY = this.mc.activeCamera.getY(partialTick);
@@ -866,7 +866,7 @@ public class WorldRenderer {
                         } else {
                             brightness = world.getLightBrightness(queryPos.set(x, i3, z));
                         }
-                        tessellator.setColor4f(brightness, brightness, brightness, ((1.0F - f11 * f11) * 0.3F + 0.5F) * world.weatherManager.getWeatherIntensity() * world.weatherManager.getWeatherPower());
+                        tessellator.setColor4f(brightness, brightness, brightness, ((1.0F - f11 * f11) * 0.3F + 0.5F) * world.getWeatherManager().getWeatherIntensity() * world.getWeatherManager().getWeatherPower());
                         tessellator.setTranslation(-nextPosX, -nextPosY, -nextPosZ);
                         tessellator.addVertexWithUV(x, k3, (double) z + 0.5D, 0.0F * f3 + f7, ((float) k3 * f3) / 4F + f6 * f3 + f8);
                         tessellator.addVertexWithUV(x + 1, k3, (double) z + 0.5D, f3 + f7, ((float) k3 * f3) / 4F + f6 * f3 + f8);
@@ -917,7 +917,7 @@ public class WorldRenderer {
                             brightness = world.getLightBrightness(queryPos.set(x, world.getHeightBlocks(), z)) * 0.85F + 0.15F;
                         }
                         tessellator.setTranslation(-nextPosX, -nextPosY, -nextPosZ);
-                        tessellator.setColor4f(brightness, brightness, brightness, ((1.0F - f9 * f9) * 0.5F + 0.5F) * (world.weatherManager.getWeatherIntensity() - 0.1f) * world.weatherManager.getWeatherPower());
+                        tessellator.setColor4f(brightness, brightness, brightness, ((1.0F - f9 * f9) * 0.5F + 0.5F) * (world.getWeatherManager().getWeatherIntensity() - 0.1f) * world.getWeatherManager().getWeatherPower());
                         tessellator.addVertexWithUV(x, j3, (double) z + 0.5D, 0.0F * f2, ((float) j3 * f2) / 4F + f4 * f2);
                         tessellator.addVertexWithUV(x + 1, j3, (double) z + 0.5D, f2, ((float) j3 * f2) / 4F + f4 * f2);
                         tessellator.addVertexWithUV(x + 1, l3, (double) z + 0.5D, f2, ((float) l3 * f2) / 4F + f4 * f2);
@@ -965,7 +965,7 @@ public class WorldRenderer {
                         } else {
                             brightness = world.getLightBrightness(queryPos.set(x, world.getHeightBlocks(), z)) * 0.85F + 0.15F;
                         }
-                        tessellator.setColor4f(brightness, brightness, brightness, ((1.0F - f9 * f9) * 0.5F + 0.5F) * (world.weatherManager.getWeatherIntensity() - 0.1f) * world.weatherManager.getWeatherPower());
+                        tessellator.setColor4f(brightness, brightness, brightness, ((1.0F - f9 * f9) * 0.5F + 0.5F) * (world.getWeatherManager().getWeatherIntensity() - 0.1f) * world.getWeatherManager().getWeatherPower());
                         tessellator.setTranslation(-nextPosX, -nextPosY, -nextPosZ);
                         tessellator.addVertexWithUV(x, j3, (double) z + 0.5D, 0.0F * f2, ((float) j3 * f2) / 4F - vOffset * f2);
                         tessellator.addVertexWithUV(x + 1, j3, (double) z + 0.5D, f2, ((float) j3 * f2) / 4F - vOffset * f2);
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/RenderBlocks.java b/game/client/src/main/java/net/minecraft/client/render/block/model/RenderBlocks.java
index 3a8eb749c..f29dc48b6 100644
--- a/game/client/src/main/java/net/minecraft/client/render/block/model/RenderBlocks.java
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/RenderBlocks.java
@@ -1,5 +1,6 @@
 package net.minecraft.client.render.block.model;
 
+import net.minecraft.client.option.GameSettings;
 import net.minecraft.client.render.LightingCache;
 import net.minecraft.client.render.tessellator.TessellatorGeneral;
 import net.minecraft.core.util.helper.LightIndexHelper;
@@ -247,7 +248,7 @@ public final class RenderBlocks {
                               int dirX, int dirY, int dirZ, float depth,
                               int topX, int topY, int topZ, float topP, float botP,
                               int lefX, int lefY, int lefZ, float lefP, float rigP) {
-        boolean ao = this.mc.isAmbientOcclusionEnabled() && block.emission == 0;
+        boolean ao = GameSettings.AMBIENT_OCCLUSION.value && block.emission == 0;
         boolean isFullCube = depth <= FULL_CUBE_THRESHOLD;
 
         if (ao) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/colorizer/Colorizers.java b/game/client/src/main/java/net/minecraft/client/render/colorizer/Colorizers.java
index 5766e384f..e0557a2bf 100644
--- a/game/client/src/main/java/net/minecraft/client/render/colorizer/Colorizers.java
+++ b/game/client/src/main/java/net/minecraft/client/render/colorizer/Colorizers.java
@@ -98,7 +98,7 @@ public class Colorizers {
 			return;
 		}
 		
-		Season currentSeason = world.seasonManager.getCurrentSeason();
+		Season currentSeason = world.getSeasonManager().getCurrentSeason();
 		Dimension currentDimension = world.dimension;
 		float seasonProgress = getSeasonProgress(world);
 		
@@ -243,7 +243,7 @@ public class Colorizers {
 	}
 	
 	public static float getSeasonProgress(World world) {
-		if(world.seasonManager instanceof SeasonManagerCycle seasonManager) {
+		if(world.getSeasonManager() instanceof SeasonManagerCycle seasonManager) {
             Season currentSeason = seasonManager.getCurrentSeason();
 			
 			int dayInSeason = seasonManager.getDayInSeason();
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/MobRenderer.java b/game/client/src/main/java/net/minecraft/client/render/entity/MobRenderer.java
index ffc89c256..039dea3ca 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/MobRenderer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/MobRenderer.java
@@ -12,6 +12,7 @@ import net.minecraft.client.render.renderer.State;
 import net.minecraft.client.render.tessellator.TessellatorGeneral;
 import net.minecraft.core.entity.AgedMob;
 import net.minecraft.core.entity.Mob;
+import net.minecraft.core.player.gamemode.Gamemodes;
 import net.minecraft.core.util.helper.Color;
 import net.minecraft.core.util.helper.MathHelper;
 import org.jetbrains.annotations.NotNull;
@@ -161,7 +162,9 @@ public abstract class MobRenderer<T extends Mob> extends EntityRenderer<T> {
     }
 
     protected void renderSpecials(final @NotNull TessellatorGeneral tessellator, final @NotNull T entity, final double x, final double y, final double z) {
-        if (Minecraft.getMinecraft().canRenderEntityLabel()) {
+        final @NotNull Minecraft mc = Minecraft.getMinecraft();
+
+        if (GameSettings.SHOW_DEBUG_SCREEN.value && mc.thePlayer != null && mc.thePlayer.getGamemode() == Gamemodes.CREATIVE) {
             renderLivingLabel(tessellator, entity, Integer.toString(entity.id), x, y, z, 64, false);
         } else if (!entity.nickname.isEmpty()) {
             renderLivingLabel(tessellator, entity, entity.getDisplayName(), x, y, z, 64, true);
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/ShadersRenderer.java b/game/client/src/main/java/net/minecraft/client/render/shader/ShadersRenderer.java
index d40b63b47..9e056fdec 100644
--- a/game/client/src/main/java/net/minecraft/client/render/shader/ShadersRenderer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/ShadersRenderer.java
@@ -4,31 +4,29 @@ import com.mojang.logging.LogUtils;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.option.GameSettings;
 import net.minecraft.client.option.enums.CloudQuality;
-import net.minecraft.client.render.FrameBuffer;
 import net.minecraft.client.render.LightmapHelper;
 import net.minecraft.client.render.OpenGLHelper;
 import net.minecraft.client.render.Renderer;
 import net.minecraft.client.render.camera.CameraUtil;
 import net.minecraft.client.render.renderer.Shaders;
-import net.minecraft.client.render.texture.Texture;
+import net.minecraft.client.render.shader.framebuffer.*;
 import net.minecraft.core.block.material.Materials;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
 import org.lwjgl.opengl.GL41;
 import org.slf4j.Logger;
 
 import java.io.File;
+import java.util.Objects;
 
 public class ShadersRenderer implements Renderer {
     private static final Logger LOGGER = LogUtils.getLogger();
     public Minecraft mc;
 
-    protected final FrameBuffer worldFramebuffer = new FrameBuffer();
-    protected final Texture worldFramebufferColor = new Texture();
-    protected final Texture worldFramebufferDepth = new Texture();
-    protected int worldRenderBufferObject = 0;
-
-    protected final FrameBuffer gameFramebuffer = new FrameBuffer();
-    protected final Texture gameFramebufferColor = new Texture();
-    protected int gameRenderBufferObject = 0;
+    protected @Nullable FrameBuffer worldFrameBuffer = null;
+    protected @Nullable FrameBuffer gameFrameBuffer = null;
 
     protected final Shader postShader = new Shader();
     protected final Shader finalShader = new Shader();
@@ -50,10 +48,13 @@ public class ShadersRenderer implements Renderer {
 
         setupFramebuffer();
 
+        assert this.worldFrameBuffer != null;
+        assert this.gameFrameBuffer != null;
+
         if (this.finalShader.isEnabled()) {
-            this.gameFramebuffer.bind();
+            this.gameFrameBuffer.bind();
         } else {
-            GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, 0);
+            this.gameFrameBuffer.unbind();
         }
 
         GL41.glUseProgram(0);
@@ -66,21 +67,30 @@ public class ShadersRenderer implements Renderer {
     public void endRenderGame(float partialTicks) {
         OpenGLHelper.checkError("pre end render game");
 
-        GL41.glViewport(0, 0, this.fbWidth, this.fbHeight);
-        GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, 0);
+        assert this.gameFrameBuffer != null;
+
+        this.gameFrameBuffer.blit();
+        final @NotNull FrameBuffer outputBuffer = this.gameFrameBuffer.getOutputBuffer();
 
         if (this.finalShader.isEnabled()) {
             this.finalShader.bind();
 
             GL41.glActiveTexture(GL41.GL_TEXTURE0);
-            this.gameFramebufferColor.bind();
+            Objects.requireNonNull(outputBuffer.getAttachment(FrameBuffer.AttachmentType.COLOR)).bindForOutput();
             this.finalShader.uniformInt("colortex0", 0);
 
             ShaderHelper.setUniforms(this.mc, this.finalShader, partialTicks);
-            ShaderHelper.drawFullscreenRect();
+        } else {
             GL41.glUseProgram(0);
+
+            Objects.requireNonNull(outputBuffer.getAttachment(FrameBuffer.AttachmentType.COLOR)).bindForOutput();
         }
 
+        GL41.glViewport(0, 0, this.fbWidth, this.fbHeight);
+        ShaderHelper.drawFullscreenRect();
+
+        GL41.glUseProgram(0);
+
         OpenGLHelper.checkError("end render game");
     }
 
@@ -88,8 +98,10 @@ public class ShadersRenderer implements Renderer {
     public void beginRenderWorld(float partialTicks) {
         OpenGLHelper.checkError("pre begin render world");
 
+        assert this.worldFrameBuffer != null;
+
         setupFramebuffer();
-        this.worldFramebuffer.bind();
+        this.worldFrameBuffer.bind();
         GL41.glViewport(0, 0, this.renderWidth, this.renderHeight);
 
         GL41.glUseProgram(0);
@@ -101,21 +113,23 @@ public class ShadersRenderer implements Renderer {
     public void endRenderWorld(float partialTicks) {
         OpenGLHelper.checkError("pre end render world");
 
-        if (this.finalShader.isEnabled()) {
-            this.gameFramebuffer.bind();
-        } else {
-            GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, 0);
-        }
+        assert this.worldFrameBuffer != null;
+        assert this.gameFrameBuffer != null;
+
+        this.worldFrameBuffer.blit();
+        final @NotNull FrameBuffer outputBuffer = this.worldFrameBuffer.getOutputBuffer();
 
         if (this.postShader.isEnabled()) {
             this.postShader.bind();
 
             GL41.glActiveTexture(GL41.GL_TEXTURE0);
-            this.worldFramebufferColor.bind();
+            Objects.requireNonNull(outputBuffer.getAttachment(FrameBuffer.AttachmentType.COLOR)).bindForOutput();
             this.postShader.uniformInt("colortex0", 0);
+
             GL41.glActiveTexture(GL41.GL_TEXTURE1);
-            this.worldFramebufferDepth.bind();
+            Objects.requireNonNull(outputBuffer.getAttachment(FrameBuffer.AttachmentType.DEPTH)).bindForOutput();
             this.postShader.uniformInt("depthtex0", 1);
+
             GL41.glActiveTexture(GL41.GL_TEXTURE0);
 
             this.mc.ppm.enabled = true;
@@ -123,7 +137,14 @@ public class ShadersRenderer implements Renderer {
             this.mc.ppm.enabled = false;
         } else {
             GL41.glUseProgram(0);
-            this.worldFramebufferColor.bind();
+
+            Objects.requireNonNull(outputBuffer.getAttachment(FrameBuffer.AttachmentType.COLOR)).bindForOutput();
+        }
+
+        if (this.finalShader.isEnabled()) {
+            this.gameFrameBuffer.bind();
+        } else {
+            this.gameFrameBuffer.unbind();
         }
 
         GL41.glViewport(0, 0, this.fbWidth, this.fbHeight);
@@ -132,7 +153,6 @@ public class ShadersRenderer implements Renderer {
         LightmapHelper.instance.enableLightmapRendering();
 
         GL41.glUseProgram(0);
-//        GL41.glEnable(GL41.GL_ALPHA_TEST);
 
         OpenGLHelper.checkError("end render world");
     }
@@ -181,62 +201,69 @@ public class ShadersRenderer implements Renderer {
         int scaledWidth = (int) (renderScale * this.mc.gameWindow.getWidthPixels());
         int scaledHeight = (int) (renderScale * this.mc.gameWindow.getHeightPixels());
 
-        if (this.fbWidth != this.mc.gameWindow.getWidthPixels() || this.fbHeight != this.mc.gameWindow.getHeightPixels() || this.renderWidth != scaledWidth || this.renderHeight != scaledHeight || !this.gameFramebuffer.isGenerated() || !this.worldFramebuffer.isGenerated()) {
-            this.worldFramebuffer.generate();
-            this.worldFramebufferColor.generate();
-            this.worldFramebufferDepth.generate();
-            this.worldRenderBufferObject = GL41.glGenRenderbuffers();
-            this.gameFramebuffer.generate();
-            this.gameFramebufferColor.generate();
-            this.gameRenderBufferObject = GL41.glGenRenderbuffers();
-
+        if (
+            this.worldFrameBuffer == null ||
+            this.gameFrameBuffer == null ||
+            this.fbWidth != this.mc.gameWindow.getWidthPixels() || this.fbHeight != this.mc.gameWindow.getHeightPixels() ||
+            this.renderWidth != scaledWidth || this.renderHeight != scaledHeight
+        ) {
             this.fbWidth = this.mc.gameWindow.getWidthPixels();
             this.fbHeight = this.mc.gameWindow.getHeightPixels();
 
             this.renderWidth = scaledWidth;
             this.renderHeight = scaledHeight;
 
-            boolean filtering = GameSettings.RENDER_SCALE.value.useLinearFiltering;
-            int filterMode = filtering ? GL41.GL_LINEAR : GL41.GL_NEAREST;
-
-            LOGGER.info("Display: {} x {}, Render: {} x {}", this.fbWidth, this.fbHeight, this.renderWidth, this.renderHeight);
-
-            this.worldFramebuffer.bind();
-            this.worldFramebufferColor.bind();
-            GL41.glTexImage2D(GL41.GL_TEXTURE_2D, 0, GL41.GL_RGBA, this.renderWidth, this.renderHeight, 0, GL41.GL_RGBA, GL41.GL_UNSIGNED_BYTE, 0);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_MIN_FILTER, filterMode);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_MAG_FILTER, filterMode);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_WRAP_S, GL41.GL_CLAMP_TO_EDGE);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_WRAP_T, GL41.GL_CLAMP_TO_EDGE);
-            GL41.glFramebufferTexture2D(GL41.GL_FRAMEBUFFER, GL41.GL_COLOR_ATTACHMENT0, GL41.GL_TEXTURE_2D, this.worldFramebufferColor.id(), 0);
-
-            this.worldFramebufferDepth.bind();
-            GL41.glTexImage2D(GL41.GL_TEXTURE_2D, 0, GL41.GL_DEPTH_COMPONENT, this.renderWidth, this.renderHeight, 0, GL41.GL_DEPTH_COMPONENT, GL41.GL_FLOAT, 0);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_MIN_FILTER, filterMode);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_MAG_FILTER, filterMode);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_WRAP_S, GL41.GL_CLAMP_TO_EDGE);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_WRAP_T, GL41.GL_CLAMP_TO_EDGE);
-
-            GL41.glFramebufferTexture2D(GL41.GL_FRAMEBUFFER, GL41.GL_DEPTH_ATTACHMENT, GL41.GL_TEXTURE_2D, this.worldFramebufferDepth.id(), 0);
-
-//            GL41.glBindRenderbuffer(GL41.GL_RENDERBUFFER, this.worldRenderBufferObject);
-//            GL41.glRenderbufferStorage(GL41.GL_RENDERBUFFER, GL41.GL_DEPTH24_STENCIL8, this.renderWidth, this.renderHeight);
-//            GL41.glFramebufferRenderbuffer(GL41.GL_FRAMEBUFFER, GL41.GL_DEPTH_STENCIL_ATTACHMENT, GL41.GL_RENDERBUFFER, this.worldRenderBufferObject);
-
-            this.gameFramebuffer.bind();
-            this.gameFramebufferColor.bind();
-            GL41.glTexImage2D(GL41.GL_TEXTURE_2D, 0, GL41.GL_RGBA, this.fbWidth, this.fbHeight, 0, GL41.GL_RGBA, GL41.GL_UNSIGNED_BYTE, 0);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_MIN_FILTER, filterMode);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_MAG_FILTER, filterMode);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_WRAP_S, GL41.GL_CLAMP_TO_EDGE);
-            GL41.glTexParameteri(GL41.GL_TEXTURE_2D, GL41.GL_TEXTURE_WRAP_T, GL41.GL_CLAMP_TO_EDGE);
-            GL41.glFramebufferTexture2D(GL41.GL_FRAMEBUFFER, GL41.GL_COLOR_ATTACHMENT0, GL41.GL_TEXTURE_2D, this.gameFramebufferColor.id(), 0);
-
-            GL41.glBindRenderbuffer(GL41.GL_RENDERBUFFER, this.gameRenderBufferObject);
-            GL41.glRenderbufferStorage(GL41.GL_RENDERBUFFER, GL41.GL_DEPTH24_STENCIL8, this.fbWidth, this.fbHeight);
-            GL41.glFramebufferRenderbuffer(GL41.GL_FRAMEBUFFER, GL41.GL_DEPTH_STENCIL_ATTACHMENT, GL41.GL_RENDERBUFFER, this.gameRenderBufferObject);
-
-            GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, 0);
+            final boolean filtering = GameSettings.RENDER_SCALE.value.useLinearFiltering;
+            final int filterMode = filtering ? GL41.GL_LINEAR : GL41.GL_NEAREST;
+
+            final @NotNull Vector2ic fbSize = new Vector2i(this.fbWidth, this.fbHeight);
+            final @NotNull Vector2ic renderSize = new Vector2i(this.renderWidth, this.renderHeight);
+
+            LOGGER.info("Display: {}, Render: {}", fbSize, renderSize);
+
+            this.worldFrameBuffer = new FrameBufferSingleSample(
+                new FrameBufferTextureSingleSample(
+                    GL41.GL_RGBA,
+                    GL41.GL_COLOR_ATTACHMENT0,
+                    renderSize,
+                    parameters -> {
+                        parameters.set(GL41.GL_TEXTURE_MIN_FILTER, filterMode);
+                        parameters.set(GL41.GL_TEXTURE_MAG_FILTER, filterMode);
+                        parameters.set(GL41.GL_TEXTURE_WRAP_S, GL41.GL_CLAMP_TO_EDGE);
+                        parameters.set(GL41.GL_TEXTURE_WRAP_T, GL41.GL_CLAMP_TO_EDGE);
+                    }
+                ),
+                new FrameBufferTextureSingleSample(
+                    GL41.GL_DEPTH_COMPONENT,
+                    GL41.GL_DEPTH_ATTACHMENT,
+                    renderSize,
+                    parameters -> {
+                        parameters.set(GL41.GL_TEXTURE_MIN_FILTER, filterMode);
+                        parameters.set(GL41.GL_TEXTURE_MAG_FILTER, filterMode);
+                        parameters.set(GL41.GL_TEXTURE_WRAP_S, GL41.GL_CLAMP_TO_EDGE);
+                        parameters.set(GL41.GL_TEXTURE_WRAP_T, GL41.GL_CLAMP_TO_EDGE);
+                    }
+                )
+            );
+
+            this.gameFrameBuffer = new FrameBufferSingleSample(
+                new FrameBufferTextureSingleSample(
+                    GL41.GL_RGBA,
+                    GL41.GL_COLOR_ATTACHMENT0,
+                    fbSize,
+                    parameters -> {
+                        parameters.set(GL41.GL_TEXTURE_MIN_FILTER, filterMode);
+                        parameters.set(GL41.GL_TEXTURE_MAG_FILTER, filterMode);
+                        parameters.set(GL41.GL_TEXTURE_WRAP_S, GL41.GL_CLAMP_TO_EDGE);
+                        parameters.set(GL41.GL_TEXTURE_WRAP_T, GL41.GL_CLAMP_TO_EDGE);
+                    }
+                ),
+                new RenderBufferSingleSample(
+                    GL41.GL_DEPTH24_STENCIL8,
+                    GL41.GL_DEPTH_STENCIL_ATTACHMENT,
+                    fbSize
+                )
+            );
         }
     }
 
@@ -253,8 +280,9 @@ public class ShadersRenderer implements Renderer {
 
         ShaderProvider shaderProvider = getShader();
 
+        this.finalShader.compile(internal, "post/base");
+
         if (shaderProvider != null) {
-            this.finalShader.compile(shaderProvider, "post/base");
             this.postShader.compile(shaderProvider, "post/post");
         }
 
@@ -263,14 +291,15 @@ public class ShadersRenderer implements Renderer {
 
     @Override
     public void delete() {
-        this.gameFramebuffer.delete();
-        GL41.glDeleteRenderbuffers(this.gameRenderBufferObject);
-        this.gameFramebufferColor.delete();
-
-        this.worldFramebuffer.delete();
-        GL41.glDeleteRenderbuffers(this.worldRenderBufferObject);
-        this.worldFramebufferColor.delete();
-        this.worldFramebufferDepth.delete();
+        if (this.gameFrameBuffer != null) {
+            this.gameFrameBuffer.delete();
+            this.gameFrameBuffer = null;
+        }
+
+        if (this.worldFrameBuffer != null) {
+            this.worldFrameBuffer.delete();
+            this.worldFrameBuffer = null;
+        }
     }
 
     @Override
@@ -287,8 +316,12 @@ public class ShadersRenderer implements Renderer {
         }
     }
 
-    public Texture getWorldFramebufferColor() {
-        return this.worldFramebufferColor;
+    public @Nullable FrameBuffer getWorldFrameBuffer() {
+        return this.worldFrameBuffer;
+    }
+
+    public @Nullable FrameBuffer getGameFrameBuffer() {
+        return this.gameFrameBuffer;
     }
 
     @Override
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBuffer.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBuffer.java
new file mode 100644
index 000000000..c787d848f
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBuffer.java
@@ -0,0 +1,183 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
+import org.lwjgl.opengl.GL41;
+
+import java.util.Map;
+
+public abstract class FrameBuffer {
+    public enum AttachmentType {
+        COLOR,
+        DEPTH,
+        STENCIL;
+    }
+
+    private final int id;
+    private final @NotNull Map<@NotNull AttachmentType, @NotNull FrameBufferAttachment> attachments = new Object2ObjectArrayMap<>();
+
+    private final @NotNull Vector2ic size;
+
+    private boolean deleted = false;
+
+    protected FrameBuffer(final @NotNull FrameBufferAttachment @NotNull ... attachments) {
+        if (attachments.length == 0) {
+            throw new IllegalArgumentException("Frame buffer must have at least one attachment!");
+        }
+
+        this.size = new Vector2i(attachments[0].getSize());
+        for (int i = 1; i < attachments.length; i++) {
+            if (!this.size.equals(attachments[i].getSize())) {
+                throw new IllegalArgumentException("Size mismatch between attachments 0 and " + i + "!");
+            }
+        }
+
+        final int maxColorAttachments = GL41.glGetInteger(GL41.GL_MAX_COLOR_ATTACHMENTS);
+        for (final @NotNull FrameBufferAttachment attachment : attachments) {
+            final int attachmentTypeId = attachment.getAttachment();
+            if (attachmentTypeId >= GL41.GL_COLOR_ATTACHMENT0 && attachmentTypeId < GL41.GL_COLOR_ATTACHMENT0 + maxColorAttachments) {
+                if (this.attachments.containsKey(AttachmentType.COLOR)) {
+                    throw new IllegalArgumentException("Multiple color attachments!");
+                }
+
+                this.attachments.put(AttachmentType.COLOR, attachment);
+            }
+
+            if (attachmentTypeId == GL41.GL_DEPTH_ATTACHMENT || attachmentTypeId == GL41.GL_STENCIL_ATTACHMENT || attachmentTypeId == GL41.GL_DEPTH_STENCIL_ATTACHMENT) {
+                if (attachmentTypeId == GL41.GL_DEPTH_ATTACHMENT || attachmentTypeId == GL41.GL_DEPTH_STENCIL_ATTACHMENT) {
+                    if (this.attachments.containsKey(AttachmentType.DEPTH)) {
+                        throw new IllegalArgumentException("Multiple depth attachments!");
+                    }
+
+                    this.attachments.put(AttachmentType.DEPTH, attachment);
+                }
+
+                if (attachmentTypeId == GL41.GL_STENCIL_ATTACHMENT || attachmentTypeId == GL41.GL_DEPTH_STENCIL_ATTACHMENT) {
+                    if (this.attachments.containsKey(AttachmentType.STENCIL)) {
+                        throw new IllegalArgumentException("Multiple stencil attachments!");
+                    }
+
+                    this.attachments.put(AttachmentType.STENCIL, attachment);
+                }
+            }
+        }
+
+        this.id = GL41.glGenFramebuffers();
+
+        for (final @NotNull FrameBufferAttachment attachment : attachments) {
+            attachment.attachToFramebuffer(this);
+        }
+    }
+
+    public @Nullable FrameBufferAttachment getAttachment(final @NotNull AttachmentType attachmentType) {
+        return this.attachments.get(attachmentType);
+    }
+
+    public int getId() {
+        if (this.deleted) {
+            throw new IllegalStateException("Frame buffer deleted!");
+        }
+
+        return this.id;
+    }
+
+    public void delete() {
+        if (this.deleted) {
+            throw new IllegalStateException("Frame buffer already deleted!");
+        }
+
+        GL41.glDeleteFramebuffers(this.id);
+        this.deleted = true;
+
+        for (final @NotNull FrameBufferAttachment attachment : this.attachments.values()) {
+            if (!attachment.isDeleted()) {
+                attachment.delete();
+            }
+        }
+    }
+
+    public boolean isDeleted() {
+        return this.deleted;
+    }
+
+    public boolean isComplete() {
+        return GL41.glCheckFramebufferStatus(this.id) == GL41.GL_FRAMEBUFFER_COMPLETE;
+    }
+
+    private void checkComplete() {
+        final int status = GL41.glCheckFramebufferStatus(GL41.GL_FRAMEBUFFER);
+
+        if (status != GL41.GL_FRAMEBUFFER_COMPLETE) {
+            final @NotNull String message = switch (status) {
+                case GL41.GL_FRAMEBUFFER_UNDEFINED -> "GL_FRAMEBUFFER_UNDEFINED: Default framebuffer does not exist!";
+                case GL41.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT -> "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: One or more attachment points is incomplete!";
+                case GL41.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT -> "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: Framebuffer does not have at least one image attached!";
+                case GL41.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER -> "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: GL_DRAW_BUFFERi color attachment point is GL_NONE!";
+                case GL41.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER -> "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: GL_READ_FRAMEBUFFER color attachment point is GL_NONE!";
+                case GL41.GL_FRAMEBUFFER_UNSUPPORTED -> "GL_FRAMEBUFFER_UNSUPPORTED: Combination of attached images' formats violates implementation-dependent restrictions!";
+                case GL41.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE -> "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: Inconsistent sampling levels between color and depth attachments!";
+                case GL41.GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS -> "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: Inconsistent layering between color and depth attachments!";
+                default -> String.format("0x%04x", status) + ": Unknown framebuffer completion error!";
+            };
+
+            throw new IllegalStateException("Tried using an incomplete frame buffer! Root cause: \"" + message + "\"");
+        }
+    }
+
+    public @NotNull Vector2ic getSize() {
+        if (this.deleted) {
+            throw new IllegalStateException("Frame buffer deleted!");
+        }
+
+        return this.size;
+    }
+
+    public abstract @NotNull FrameBuffer getOutputBuffer();
+
+    public void whileBound(final @NotNull Runnable runnable) {
+        if (this.deleted) {
+            throw new IllegalStateException("Frame buffer deleted!");
+        }
+
+        GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, this.id);
+        runnable.run();
+        GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, 0);
+    }
+
+    public final void blit() {
+        if (this.deleted) {
+            throw new IllegalStateException("Frame buffer deleted!");
+        }
+
+        this.whileBound(this::checkComplete);
+
+        this.blitInner();
+    }
+
+    public final void bind() {
+        if (this.deleted) {
+            throw new IllegalStateException("Frame buffer deleted!");
+        }
+
+        this.whileBound(this::checkComplete);
+
+        this.bindInner();
+    }
+
+    protected abstract void bindInner();
+
+    public final void unbind() {
+        if (this.deleted) {
+            throw new IllegalStateException("Frame buffer deleted!");
+        }
+
+        this.whileBound(this::checkComplete);
+
+        GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, 0);
+    }
+
+    protected abstract void blitInner();
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferAttachment.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferAttachment.java
new file mode 100644
index 000000000..36dd95815
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferAttachment.java
@@ -0,0 +1,24 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector2ic;
+
+public interface FrameBufferAttachment {
+    int getId();
+
+    @NotNull Vector2ic getSize();
+
+    void delete();
+
+    boolean isDeleted();
+
+    void whileBound(final @NotNull Runnable runnable);
+
+    void attachToFramebuffer(final @NotNull FrameBuffer frameBuffer);
+
+    int getAttachment();
+
+    void bindForOutput();
+
+    void unbindForOutput();
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferAttachmentMultiSample.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferAttachmentMultiSample.java
new file mode 100644
index 000000000..2393cb7fa
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferAttachmentMultiSample.java
@@ -0,0 +1,4 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+public interface FrameBufferAttachmentMultiSample extends FrameBufferAttachment {
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferAttachmentSingleSample.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferAttachmentSingleSample.java
new file mode 100644
index 000000000..99c49aef3
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferAttachmentSingleSample.java
@@ -0,0 +1,4 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+public interface FrameBufferAttachmentSingleSample extends FrameBufferAttachment {
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferMultiSample.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferMultiSample.java
new file mode 100644
index 000000000..43f2ab10a
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferMultiSample.java
@@ -0,0 +1,59 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector2ic;
+import org.lwjgl.opengl.GL41;
+
+public class FrameBufferMultiSample extends FrameBuffer {
+    final @NotNull FrameBufferSingleSample blitBuffer;
+
+    public FrameBufferMultiSample(final @NotNull FrameBufferAttachmentMultiSample @NotNull ... attachments) {
+        super(attachments);
+
+        this.blitBuffer = new FrameBufferSingleSample(
+            new FrameBufferTextureSingleSample(
+                GL41.GL_RGBA,
+                GL41.GL_COLOR_ATTACHMENT0,
+                this.getSize(),
+                parameters -> {
+                    parameters.set(GL41.GL_TEXTURE_MIN_FILTER, GL41.GL_NEAREST);
+                    parameters.set(GL41.GL_TEXTURE_MAG_FILTER, GL41.GL_NEAREST);
+                    parameters.set(GL41.GL_TEXTURE_WRAP_S, GL41.GL_CLAMP_TO_EDGE);
+                    parameters.set(GL41.GL_TEXTURE_WRAP_T, GL41.GL_CLAMP_TO_EDGE);
+                }
+            ),
+            new FrameBufferTextureSingleSample(
+                GL41.GL_DEPTH_COMPONENT,
+                GL41.GL_DEPTH_ATTACHMENT,
+                this.getSize(),
+                parameters -> {
+                    parameters.set(GL41.GL_TEXTURE_MIN_FILTER, GL41.GL_NEAREST);
+                    parameters.set(GL41.GL_TEXTURE_MAG_FILTER, GL41.GL_NEAREST);
+                    parameters.set(GL41.GL_TEXTURE_WRAP_S, GL41.GL_CLAMP_TO_EDGE);
+                    parameters.set(GL41.GL_TEXTURE_WRAP_T, GL41.GL_CLAMP_TO_EDGE);
+                }
+            )
+        );
+    }
+
+    @Override
+    public @NotNull FrameBuffer getOutputBuffer() {
+        return this.blitBuffer;
+    }
+
+    @Override
+    protected void blitInner() {
+        final @NotNull Vector2ic size = this.getSize();
+
+        GL41.glBindFramebuffer(GL41.GL_READ_FRAMEBUFFER, this.getId());
+        GL41.glBindFramebuffer(GL41.GL_DRAW_FRAMEBUFFER, this.blitBuffer.getId());
+        GL41.glBlitFramebuffer(0, 0, size.x(), size.y(), 0, 0, size.x(), size.y(), GL41.GL_COLOR_BUFFER_BIT, GL41.GL_NEAREST);
+        GL41.glBindFramebuffer(GL41.GL_READ_FRAMEBUFFER, 0);
+        GL41.glBindFramebuffer(GL41.GL_DRAW_FRAMEBUFFER, 0);
+    }
+
+    @Override
+    protected void bindInner() {
+        GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, this.getId());
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferSingleSample.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferSingleSample.java
new file mode 100644
index 000000000..6c05487aa
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferSingleSample.java
@@ -0,0 +1,25 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import org.jetbrains.annotations.NotNull;
+import org.lwjgl.opengl.GL41;
+
+public class FrameBufferSingleSample extends FrameBuffer {
+    public FrameBufferSingleSample(final @NotNull FrameBufferAttachmentSingleSample @NotNull ... attachments) {
+        super(attachments);
+    }
+
+    @Override
+    protected void blitInner() {
+        // Do nothing
+    }
+
+    @Override
+    protected void bindInner() {
+        GL41.glBindFramebuffer(GL41.GL_FRAMEBUFFER, this.getId());
+    }
+
+    @Override
+    public @NotNull FrameBuffer getOutputBuffer() {
+        return this;
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferTexture.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferTexture.java
new file mode 100644
index 000000000..8462482f1
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferTexture.java
@@ -0,0 +1,129 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import net.minecraft.client.GLAllocation;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
+import org.lwjgl.opengl.GL41;
+
+import java.util.function.Consumer;
+
+public abstract class FrameBufferTexture implements FrameBufferAttachment {
+    private final int id;
+    private final int attachment;
+    private final @NotNull Vector2ic size;
+
+    private boolean deleted = false;
+
+    public FrameBufferTexture(final int format, final int attachment, final @NotNull Vector2ic size, final int samples, final @Nullable Consumer<@NotNull Parameters> parametersConsumer) {
+        this.id = GLAllocation.generateTexture();
+        this.attachment = attachment;
+        this.size = new Vector2i(size);
+
+        this.whileBound(() -> {
+            this.init(format, size, samples);
+            if (parametersConsumer != null) {
+                parametersConsumer.accept(new Parameters(this.getTextureTarget()));
+            }
+        });
+    }
+
+    @Override
+    public int getId() {
+        if (this.deleted) {
+            throw new IllegalStateException("Texture deleted!");
+        }
+
+        return this.id;
+    }
+
+    @Override
+    public @NotNull Vector2ic getSize() {
+        if (this.deleted) {
+            throw new IllegalStateException("Texture deleted!");
+        }
+
+        return this.size;
+    }
+
+    @Override
+    public void delete() {
+        if (this.deleted) {
+            throw new IllegalStateException("Texture already deleted!");
+        }
+
+        GLAllocation.deleteTexture(this.id);
+        this.deleted = true;
+    }
+
+    @Override
+    public boolean isDeleted() {
+        return this.deleted;
+    }
+
+    @Override
+    public void whileBound(final @NotNull Runnable runnable) {
+        if (this.deleted) {
+            throw new IllegalStateException("Texture deleted!");
+        }
+
+        GL41.glBindTexture(this.getTextureTarget(), this.id);
+        runnable.run();
+        GL41.glBindTexture(this.getTextureTarget(), 0);
+    }
+
+    @Override
+    public void attachToFramebuffer(final @NotNull FrameBuffer frameBuffer) {
+        frameBuffer.whileBound(() -> {
+            GL41.glFramebufferTexture2D(GL41.GL_FRAMEBUFFER, this.attachment, this.getTextureTarget(), this.getId(), 0);
+        });
+    }
+
+    @Override
+    public int getAttachment() {
+        return this.attachment;
+    }
+
+    @Override
+    public void bindForOutput() {
+        GL41.glBindTexture(this.getTextureTarget(), this.id);
+    }
+
+    @Override
+    public void unbindForOutput() {
+        GL41.glBindTexture(this.getTextureTarget(), 0);
+    }
+
+    protected abstract void init(final int format, final @NotNull Vector2ic size, final int samples);
+
+    protected abstract int getTextureTarget();
+
+    public static final class Parameters {
+        private final int textureTarget;
+
+        private Parameters(final int textureTarget) {
+            this.textureTarget = textureTarget;
+        }
+
+        public @NotNull Parameters set(final int parameter, final int value) {
+            GL41.glTexParameteri(this.textureTarget, parameter, value);
+            return this;
+        }
+
+        public @NotNull Parameters set(final int parameter, final float value) {
+            GL41.glTexParameterf(this.textureTarget, parameter, value);
+            return this;
+        }
+
+        public @NotNull Parameters set(final int parameter, final int @NotNull ... values) {
+            GL41.glTexParameteriv(this.textureTarget, parameter, values);
+            return this;
+        }
+
+        public @NotNull Parameters set(final int parameter, final float @NotNull ... values) {
+            GL41.glTexParameterfv(this.textureTarget, parameter, values);
+            return this;
+        }
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferTextureMultiSample.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferTextureMultiSample.java
new file mode 100644
index 000000000..922cb7567
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferTextureMultiSample.java
@@ -0,0 +1,24 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector2ic;
+import org.lwjgl.opengl.GL41;
+
+import java.util.function.Consumer;
+
+public class FrameBufferTextureMultiSample extends FrameBufferTexture implements FrameBufferAttachmentMultiSample {
+    public FrameBufferTextureMultiSample(final int format, final int attachment, final @NotNull Vector2ic size, final int samples) {
+        super(format, attachment, size, samples, null);
+    }
+
+    @Override
+    protected void init(final int format, final @NotNull Vector2ic size, final int samples) {
+        GL41.glTexImage2DMultisample(this.getTextureTarget(), samples, format, size.x(), size.y(), true);
+    }
+
+    @Override
+    protected int getTextureTarget() {
+        return GL41.GL_TEXTURE_2D_MULTISAMPLE;
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferTextureSingleSample.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferTextureSingleSample.java
new file mode 100644
index 000000000..5f725c522
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/FrameBufferTextureSingleSample.java
@@ -0,0 +1,33 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector2ic;
+import org.lwjgl.opengl.GL41;
+
+import java.util.function.Consumer;
+
+public class FrameBufferTextureSingleSample extends FrameBufferTexture implements FrameBufferAttachmentSingleSample {
+    public FrameBufferTextureSingleSample(final int format, final int attachment, final @NotNull Vector2ic size, final @Nullable Consumer<@NotNull Parameters> parametersConsumer) {
+        super(format, attachment, size, 0, parametersConsumer);
+    }
+
+    @Override
+    protected void init(final int format, final @NotNull Vector2ic size, final int samples) {
+        final int type;
+        if (format == GL41.GL_DEPTH24_STENCIL8) {
+            type = GL41.GL_UNSIGNED_INT;
+        } else if (format == GL41.GL_DEPTH_COMPONENT) {
+            type = GL41.GL_FLOAT;
+        } else {
+            type = GL41.GL_UNSIGNED_BYTE;
+        }
+
+        GL41.glTexImage2D(this.getTextureTarget(), 0, format, size.x(), size.y(), 0, format, type, 0);
+    }
+
+    @Override
+    protected int getTextureTarget() {
+        return GL41.GL_TEXTURE_2D;
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/RenderBuffer.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/RenderBuffer.java
new file mode 100644
index 000000000..8897a32d7
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/RenderBuffer.java
@@ -0,0 +1,91 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector2ic;
+import org.lwjgl.opengl.GL41;
+
+public abstract class RenderBuffer implements FrameBufferAttachment {
+    private final int id;
+    private final int attachment;
+    private final @NotNull Vector2ic size;
+
+    private boolean deleted = false;
+
+    public RenderBuffer(final int format, final int attachment, final @NotNull Vector2ic size, final int samples) {
+        this.id = GL41.glGenRenderbuffers();
+        this.attachment = attachment;
+        this.size = size;
+
+        this.whileBound(() -> {
+            this.init(format, size, samples);
+        });
+    }
+
+    @Override
+    public int getId() {
+        if (this.deleted) {
+            throw new IllegalStateException("Render buffer deleted!");
+        }
+
+        return this.id;
+    }
+
+    @Override
+    public @NotNull Vector2ic getSize() {
+        if (this.deleted) {
+            throw new IllegalStateException("Render buffer deleted!");
+        }
+
+        return this.size;
+    }
+
+    @Override
+    public void delete() {
+        if (this.deleted) {
+            throw new IllegalStateException("Render buffer already deleted!");
+        }
+
+        GL41.glDeleteRenderbuffers(this.id);
+        this.deleted = true;
+    }
+
+    @Override
+    public boolean isDeleted() {
+        return this.deleted;
+    }
+
+    @Override
+    public void whileBound(final @NotNull Runnable runnable) {
+        if (this.deleted) {
+            throw new IllegalStateException("Render buffer deleted!");
+        }
+
+        GL41.glBindRenderbuffer(GL41.GL_RENDERBUFFER, this.id);
+        runnable.run();
+        GL41.glBindRenderbuffer(GL41.GL_RENDERBUFFER, 0);
+    }
+
+    @Override
+    public void attachToFramebuffer(final @NotNull FrameBuffer frameBuffer) {
+        frameBuffer.whileBound(() -> {
+            GL41.glFramebufferRenderbuffer(GL41.GL_FRAMEBUFFER, this.attachment, GL41.GL_RENDERBUFFER, this.id);
+        });
+    }
+
+    @Override
+    public int getAttachment() {
+        return this.attachment;
+    }
+
+    @Override
+    public void bindForOutput() {
+        throw new IllegalStateException("Render buffers cannot be bound for output!");
+    }
+
+    @Override
+    public void unbindForOutput() {
+        throw new IllegalStateException("Render buffers cannot be unbound for output!");
+    }
+
+    protected abstract void init(final int format, final @NotNull Vector2ic size, final int samples);
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/RenderBufferMultiSample.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/RenderBufferMultiSample.java
new file mode 100644
index 000000000..73a2edf95
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/RenderBufferMultiSample.java
@@ -0,0 +1,16 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector2ic;
+import org.lwjgl.opengl.GL41;
+
+public class RenderBufferMultiSample extends RenderBuffer implements FrameBufferAttachmentMultiSample {
+    public RenderBufferMultiSample(final int format, final int attachment, final @NotNull Vector2ic size, final int samples) {
+        super(format, attachment, size, samples);
+    }
+
+    @Override
+    protected void init(final int format, final @NotNull Vector2ic size, final int samples) {
+        GL41.glRenderbufferStorageMultisample(GL41.GL_RENDERBUFFER, samples, format, size.x(), size.y());
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/RenderBufferSingleSample.java b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/RenderBufferSingleSample.java
new file mode 100644
index 000000000..d28464352
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/framebuffer/RenderBufferSingleSample.java
@@ -0,0 +1,16 @@
+package net.minecraft.client.render.shader.framebuffer;
+
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector2ic;
+import org.lwjgl.opengl.GL41;
+
+public class RenderBufferSingleSample extends RenderBuffer implements FrameBufferAttachmentSingleSample {
+    public RenderBufferSingleSample(final int format, final int attachment, final @NotNull Vector2ic size) {
+        super(format, attachment, size, 0);
+    }
+
+    @Override
+    protected void init(final int format, final @NotNull Vector2ic size, final int samples) {
+        GL41.glRenderbufferStorage(GL41.GL_RENDERBUFFER, format, size.x(), size.y());
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/texture/Texture.java b/game/client/src/main/java/net/minecraft/client/render/texture/Texture.java
index 1be432f97..a5105ba62 100644
--- a/game/client/src/main/java/net/minecraft/client/render/texture/Texture.java
+++ b/game/client/src/main/java/net/minecraft/client/render/texture/Texture.java
@@ -12,6 +12,7 @@ import net.minecraft.client.option.enums.MipmapType;
 import net.minecraft.client.render.texture.meta.TextureProperties;
 import net.minecraft.client.util.helper.Buffer;
 import org.jetbrains.annotations.Nullable;
+import org.lwjgl.opengl.GL41;
 import org.slf4j.Logger;
 
 import java.awt.image.BufferedImage;
@@ -40,6 +41,10 @@ public class Texture {
 		return metaDefault;
 	}
 
+	public int getTextureType() {
+		return GL_TEXTURE_2D;
+	}
+
 	public boolean generate() {
 		if(isGenerated()) {
 			return false;
@@ -67,12 +72,12 @@ public class Texture {
 			return false;
 		}
 		
-		glBindTexture(GL_TEXTURE_2D, this.id);
+		glBindTexture(this.getTextureType(), this.id);
 		return true;
 	}
 
 	public void unbind() {
-		glBindTexture(GL_TEXTURE_2D, 0);
+		glBindTexture(this.getTextureType(), 0);
 	}
 
 	// TODO we CANNOT rely on this, especially as thing become more multithreaded this just destroys the game
@@ -87,11 +92,11 @@ public class Texture {
 	}
 	
 	public int getWidth() {
-		return glGetTexLevelParameteri(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH);
+		return glGetTexLevelParameteri(this.getTextureType(), 0, GL_TEXTURE_WIDTH);
 	}
 	
 	public int getHeight() {
-		return glGetTexLevelParameteri(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT);
+		return glGetTexLevelParameteri(this.getTextureType(), 0, GL_TEXTURE_HEIGHT);
 	}
 	
 	public int id() {
@@ -113,29 +118,31 @@ public class Texture {
 	}
 
 	public void setupTexture(BufferedImage img, boolean blurTexture, boolean clampTexture, boolean mipmap) {
+		assert this.getTextureType() == GL_TEXTURE_2D;
+
 		bind();
 
 		if(blurTexture) {
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+			glTexParameteri(this.getTextureType(), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+			glTexParameteri(this.getTextureType(), GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 		}else {
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+			glTexParameteri(this.getTextureType(), GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+			glTexParameteri(this.getTextureType(), GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 		}
 
 		if(clampTexture) {
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+			glTexParameteri(this.getTextureType(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+			glTexParameteri(this.getTextureType(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 		}else {
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+			glTexParameteri(this.getTextureType(), GL_TEXTURE_WRAP_S, GL_REPEAT);
+			glTexParameteri(this.getTextureType(), GL_TEXTURE_WRAP_T, GL_REPEAT);
 		}
 
 		int w = img.getWidth();
 		int h = img.getHeight();
 
 		Buffer.put(img);
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+		glTexImage2D(this.getTextureType(), 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
 
 		if(mipmap) {
 			generateMipmaps(buffer, img, GameSettings.MIPMAP_LEVELS.value, GameSettings.MIPMAP_TYPE.value == MipmapType.SMOOTH);
@@ -143,6 +150,8 @@ public class Texture {
 	}
 
 	protected void generateMipmaps(ByteBuffer buffer, BufferedImage image, int levels, boolean smooth) {
+		assert this.getTextureType() == GL_TEXTURE_2D;
+
 		LOGGER.info("Generating Mipmaps...");
 
 		final int width = image.getWidth();
@@ -250,10 +259,10 @@ public class Texture {
 
 			buffer.limit(buffer.position()).flip();
 
-			glTexImage2D(GL_TEXTURE_2D, debugMipmap ? 0 : level, GL_RGBA, newWidth, newHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+			glTexImage2D(this.getTextureType(), debugMipmap ? 0 : level, GL_RGBA, newWidth, newHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
 			if(!debugMipmap) {
-				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level);
-				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
+				glTexParameteri(this.getTextureType(), GL_TEXTURE_MAX_LEVEL, level);
+				glTexParameteri(this.getTextureType(), GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
 			}
 
 			data = newData;
diff --git a/game/client/src/main/java/net/minecraft/client/render/texture/TextureMultisampled.java b/game/client/src/main/java/net/minecraft/client/render/texture/TextureMultisampled.java
new file mode 100644
index 000000000..8f853e59f
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/texture/TextureMultisampled.java
@@ -0,0 +1,10 @@
+package net.minecraft.client.render.texture;
+
+import org.lwjgl.opengl.GL41;
+
+public class TextureMultisampled extends Texture {
+    @Override
+    public int getTextureType() {
+        return GL41.GL_TEXTURE_2D_MULTISAMPLE;
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/util/helper/ScreenShot.java b/game/client/src/main/java/net/minecraft/client/util/helper/ScreenShot.java
index c26e31e08..2feb5149a 100644
--- a/game/client/src/main/java/net/minecraft/client/util/helper/ScreenShot.java
+++ b/game/client/src/main/java/net/minecraft/client/util/helper/ScreenShot.java
@@ -1,12 +1,20 @@
 package net.minecraft.client.util.helper;
 
 import java.io.File;
+import java.nio.ByteBuffer;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.Objects;
+import java.util.function.Supplier;
 
 import com.mojang.logging.LogUtils;
 
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.render.shader.ShadersRenderer;
+import net.minecraft.client.render.shader.framebuffer.FrameBuffer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.opengl.GL41;
 import org.lwjgl.stb.STBImageWrite;
 import org.lwjgl.system.MemoryUtil;
@@ -17,50 +25,125 @@ public class ScreenShot
     private static final Logger LOGGER = LogUtils.getLogger();
     private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss");
 
-    public static String saveScreenshot(File mcDir, int width, int height)
-    {
-        try
-        {
-            File screenshotsDir = new File(mcDir, "screenshots");
+    private static int transferPbo = -1;
+    private static int pboWidth = -1;
+    private static int pboHeight = -1;
+
+    private static long fence = -1;
+    private static @Nullable File destFile = null;
+
+    public static void saveScreenshot(Minecraft mc, File mcDir, int width, int height) {
+        // Cancel if already screenshotting
+        if (fence >= 0) {
+            return;
+        }
+
+        // Recreate PBO if needed
+        if (transferPbo < 0 || width != pboWidth || height != pboHeight) {
+            pboWidth = width;
+            pboHeight = height;
+
+            if (transferPbo >= 0) {
+                GL41.glDeleteBuffers(transferPbo);
+            }
+
+            transferPbo = GL41.glGenBuffers();
+            GL41.glBindBuffer(GL41.GL_PIXEL_PACK_BUFFER, transferPbo);
+            GL41.glBufferData(GL41.GL_PIXEL_PACK_BUFFER, pboWidth * pboHeight * 4L, GL41.GL_STATIC_READ);
+
+            GL41.glBindBuffer(GL41.GL_PIXEL_PACK_BUFFER, 0);
+        }
+
+        try {
+            final @NotNull File screenshotsDir = new File(mcDir, "screenshots");
             //noinspection ResultOfMethodCallIgnored
             screenshotsDir.mkdir();
 
-            String dateString = dateFormat.format(new Date());
-            File destFile;
+            final @NotNull String dateString = dateFormat.format(new Date());
             int id = 1;
             while ((destFile = new File(screenshotsDir, dateString + (id != 1 ? "_" + id : "") + ".png")).exists()) {
                 id++;
             }
 
-            var buffer1 = MemoryUtil.memCalloc(width * height * 3);
-            var buffer2 = MemoryUtil.memCalloc(width * height * 3);
+            final @Nullable FrameBuffer frameBuffer = ((ShadersRenderer) mc.renderer).getGameFrameBuffer();
+            if (frameBuffer == null) {
+                throw new IllegalStateException("Game frame buffer is null!");
+            }
+
+            // Bind framebuffer and PBO
+            frameBuffer.bind();
+            GL41.glBindBuffer(GL41.GL_PIXEL_PACK_BUFFER, transferPbo);
+
+            // Insert fence
+            fence = GL41.glFenceSync(GL41.GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+
+            // Copy from FBO to PBO (asynchronous, returns immediately)
             GL41.glPixelStorei(GL41.GL_PACK_ALIGNMENT, 1);
             GL41.glPixelStorei(GL41.GL_UNPACK_ALIGNMENT, 1);
-            GL41.glReadPixels(0, 0, width, height, GL41.GL_RGB, GL41.GL_UNSIGNED_BYTE, buffer1);
+            GL41.glReadPixels(0, 0, width, height, GL41.GL_RGBA, GL41.GL_UNSIGNED_BYTE, 0);
+
+            // Unbind framebuffer and PBO
+            frameBuffer.unbind();
+            GL41.glBindBuffer(GL41.GL_PIXEL_PACK_BUFFER, 0);
 
-            for (int y = 0; y < height; y++) { // Flip image vertically
-                int index = y * width * 3;
-                buffer2.put(index, buffer1, height * width * 3 - (y + 1) * width * 3, width * 3);
+        } catch(Exception exception) {
+            LOGGER.error("Failed to save screenshot!", exception);
+        }
+    }
+
+    public static @Nullable String tickScreenShot() {
+        if (fence < 0) {
+            return null;
+        }
+
+        // Test fence result (returns immediately)
+        final int syncResult = GL41.glClientWaitSync(fence, 0, 0);
+        if (syncResult == GL41.GL_ALREADY_SIGNALED || syncResult == GL41.GL_CONDITION_SATISFIED) {
+            GL41.glDeleteSync(fence);
+            fence = -1;
+
+            // Bind PBO
+            GL41.glBindBuffer(GL41.GL_PIXEL_PACK_BUFFER, transferPbo);
+
+            // Map PBO
+            final @NotNull ByteBuffer pboBuffer = Objects.requireNonNull(GL41.glMapBuffer(GL41.GL_PIXEL_PACK_BUFFER, GL41.GL_READ_ONLY));
+
+            // Create flipped buffer
+            final @NotNull ByteBuffer flipBuffer = MemoryUtil.memCalloc(pboWidth * pboHeight * 4);
+            for (int y = 0; y < pboHeight; y++) { // Flip image vertically
+                final int index = y * pboWidth * 4;
+                flipBuffer.put(index, pboBuffer, pboHeight * pboWidth * 4 - (y + 1) * pboWidth * 4, pboWidth * 4);
+            }
+
+            if (!STBImageWrite.stbi_write_png(destFile.getAbsolutePath(), pboWidth, pboHeight, 4, flipBuffer, pboWidth * 4)) {
+                LOGGER.error("Failed to save screenshot!");
+                return "Failed to save screenshot.";
             }
 
-            STBImageWrite.stbi_write_png(destFile.getAbsolutePath(), width, height, 3, buffer2, width * 3);
+            // Free flip buffer
+            MemoryUtil.memFree(flipBuffer);
+
+            // Unmap PBO
+            GL41.glUnmapBuffer(GL41.GL_PIXEL_PACK_BUFFER);
 
-            MemoryUtil.memFree(buffer1);
-            MemoryUtil.memFree(buffer2);
+            // Unbind PBO
+            GL41.glBindBuffer(GL41.GL_PIXEL_PACK_BUFFER, 0);
 
             try {
-                boolean copiedImage = FileOpener.toClipboard(destFile.getAbsolutePath());
+                final boolean copiedImage = FileOpener.toClipboard(destFile.getAbsolutePath());
                 if (copiedImage) {
                     return "Saved screenshot as " + destFile.getName() + " and copied to clipboard.";
                 }
-            } catch (Exception ignored) {}
-            return "Saved screenshot as " + destFile.getName();
-        }
-        catch(Exception exception)
-        {
-            LOGGER.error("Failed to save screenshot!", exception);
-            return "Failed to save: " + exception;
+            } catch (final @NotNull Exception e) {
+                return "Saved screenshot as " + destFile.getName();
+            }
+        } else if (syncResult == GL41.GL_WAIT_FAILED) {
+            GL41.glDeleteSync(fence);
+            fence = -1;
+
+            throw new IllegalStateException("PBO sync wait failed!");
         }
-    }
 
+        return null;
+    }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/world/WorldClient.java b/game/client/src/main/java/net/minecraft/client/world/WorldClient.java
index add22177d..20f665cac 100644
--- a/game/client/src/main/java/net/minecraft/client/world/WorldClient.java
+++ b/game/client/src/main/java/net/minecraft/client/world/WorldClient.java
@@ -102,7 +102,7 @@ public class WorldClient extends World {
         float r = (float) (color >> 16 & 255) / 255F;
         float g = (float) (color >> 8 & 255) / 255F;
         float b = (float) (color & 255) / 255F;
-        float f6 = getCurrentWeather() != null && getCurrentWeather() instanceof IPrecipitation ? this.weatherManager.getWeatherIntensity() * this.weatherManager.getWeatherPower() : 0.0f;
+        float f6 = getCurrentWeather() != null && getCurrentWeather() instanceof IPrecipitation ? this.getWeatherManager().getWeatherIntensity() * this.getWeatherManager().getWeatherPower() : 0.0f;
         if (f6 > 0.0F) {
             float f7 = (r * 0.3F + g * 0.59F + b * 0.11F) * 0.6F;
             float f9 = 1.0F - f6 * 0.95F;
@@ -136,7 +136,7 @@ public class WorldClient extends World {
         TilePos blockPos = camera.getTilePos(partialTick);
         float blockTemperature = (float) getBlockTemperature(blockPos);
 
-        if (this.seasonManager.getCurrentSeason() != null && (this.seasonManager.getCurrentSeason() == Seasons.OVERWORLD_WINTER || this.seasonManager.getCurrentSeason() == Seasons.OVERWORLD_FALL)) {
+        if (this.getSeasonManager().getCurrentSeason() != null && (this.getSeasonManager().getCurrentSeason() == Seasons.OVERWORLD_WINTER || this.getSeasonManager().getCurrentSeason() == Seasons.OVERWORLD_FALL)) {
             blockTemperature = blockTemperature - (1.25f);
         }
         int skyColor;
@@ -151,7 +151,7 @@ public class WorldClient extends World {
         r *= f2;
         g *= f2;
         b *= f2;
-        float f7 = getCurrentWeather() != null && getCurrentWeather() instanceof IPrecipitation ? this.weatherManager.getWeatherIntensity() * this.weatherManager.getWeatherPower() : 0.0f;
+        float f7 = getCurrentWeather() != null && getCurrentWeather() instanceof IPrecipitation ? this.getWeatherManager().getWeatherIntensity() * this.getWeatherManager().getWeatherPower() : 0.0f;
         if (f7 > 0.0F) {
             float f8 = (r * 0.3F + g * 0.59F + b * 0.11F) * 0.6F;
             float f10 = 1.0F - f7 * 0.75F;
@@ -160,7 +160,7 @@ public class WorldClient extends World {
             b = b * f10 + f8 * (1.0F - f10);
         }
 
-        float fogIntensity = getCurrentWeather() != null && getCurrentWeather() == Weathers.OVERWORLD_FOG ? this.weatherManager.getWeatherIntensity() * this.weatherManager.getWeatherPower() : 0.0f;
+        float fogIntensity = getCurrentWeather() != null && getCurrentWeather() == Weathers.OVERWORLD_FOG ? this.getWeatherManager().getWeatherIntensity() * this.getWeatherManager().getWeatherPower() : 0.0f;
         if (fogIntensity > 0.0F) {
             float f8 = (r * 0.5F + g * 0.5F + b * 0.5F) * 0.6F;
             float f10 = 1.0F - fogIntensity * 0.75F;
diff --git a/game/client/src/main/java/org/useless/dragonfly/models/block/mojang/StaticBlockModelMojang.java b/game/client/src/main/java/org/useless/dragonfly/models/block/mojang/StaticBlockModelMojang.java
index 594e0819a..6087bb80a 100644
--- a/game/client/src/main/java/org/useless/dragonfly/models/block/mojang/StaticBlockModelMojang.java
+++ b/game/client/src/main/java/org/useless/dragonfly/models/block/mojang/StaticBlockModelMojang.java
@@ -1,6 +1,7 @@
 package org.useless.dragonfly.models.block.mojang;
 
 import net.minecraft.client.Minecraft;
+import net.minecraft.client.option.GameSettings;
 import net.minecraft.client.render.LightingCache;
 import net.minecraft.client.render.block.color.BlockColor;
 import net.minecraft.client.render.block.color.BlockColorDispatcher;
@@ -231,7 +232,7 @@ public class StaticBlockModelMojang implements StaticBlockModel {
                     }
                 }
 
-                final boolean useAO = this.compiled.data.ambientOcclusion && MC.isAmbientOcclusionEnabled() && element.shade;
+                final boolean useAO = this.compiled.data.ambientOcclusion && GameSettings.AMBIENT_OCCLUSION.value && element.shade;
                 final boolean isFullCube = depth <= FULL_CUBE_THRESHOLD && element.shade;
 
                 final float r;
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/MobSkeleton.java b/game/core/src/main/java/net/minecraft/core/entity/monster/MobSkeleton.java
index c1a5d656f..0d0d425ec 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/monster/MobSkeleton.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/MobSkeleton.java
@@ -75,7 +75,7 @@ public class MobSkeleton extends MobMonsterArmored<HumanArmorShape>
             float f = calcBrightness(1.0F);
             Weather weather;
             if(f > 0.5F && world.canBlockSeeTheSky(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)) && random.nextFloat() * 30F < (f - 0.4F) * 2.0F
-                && (weather = world.getCurrentWeather()) != null &&  !weather.isDamp() && (weather != Weathers.OVERWORLD_FOG || world.weatherManager.getWeatherPower() < 0.8F))
+                && (weather = world.getCurrentWeather()) != null &&  !weather.isDamp() && (weather != Weathers.OVERWORLD_FOG || world.getWeatherManager().getWeatherPower() < 0.8F))
             {
                 remainingFireTicks = 300;
             }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/MobSnowman.java b/game/core/src/main/java/net/minecraft/core/entity/monster/MobSnowman.java
index f31482606..0dec555e9 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/monster/MobSnowman.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/MobSnowman.java
@@ -33,7 +33,7 @@ public class MobSnowman extends MobMonster
         {
             float f = calcBrightness(1.0F);
             if(f > 0.5F && world.canBlockSeeTheSky(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)) && random.nextFloat() * 30F < (f - 0.4F) * 2.0F
-                    && (world.getCurrentWeather() != Weathers.OVERWORLD_FOG || world.weatherManager.getWeatherPower() < 0.75F) && world.getCurrentWeather() != Weathers.OVERWORLD_SNOW && world.getCurrentWeather() != Weathers.OVERWORLD_WINTER_SNOW)
+                    && (world.getCurrentWeather() != Weathers.OVERWORLD_FOG || world.getWeatherManager().getWeatherPower() < 0.75F) && world.getCurrentWeather() != Weathers.OVERWORLD_SNOW && world.getCurrentWeather() != Weathers.OVERWORLD_WINTER_SNOW)
             {
                 remainingFireTicks = 300;
             }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombie.java b/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombie.java
index 6c1848438..2bf4d8a41 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombie.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombie.java
@@ -29,7 +29,7 @@ public class MobZombie extends MobMonsterArmored<HumanArmorShape>
             float f = calcBrightness(1.0F);
             Weather weather = world.getCurrentWeather();
             if(f > 0.5F && world.canBlockSeeTheSky(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)) && random.nextFloat() * 30F < (f - 0.4F) * 2.0F
-                && weather != null && !weather.isDamp() && (world.getCurrentWeather() != Weathers.OVERWORLD_FOG || world.weatherManager.getWeatherPower() < 0.8F))
+                && weather != null && !weather.isDamp() && (world.getCurrentWeather() != Weathers.OVERWORLD_FOG || world.getWeatherManager().getWeatherPower() < 0.8F))
             {
                 remainingFireTicks = 300;
             }
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandChunk.java b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandChunk.java
index 51507880c..d99de6922 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandChunk.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandChunk.java
@@ -26,9 +26,7 @@ public class CommandChunk implements CommandManager.CommandRegistry {
                         int z = coordinates2D.getZ(source);
 
                         world.getChunkProvider().regenerateChunk(x, z);
-                        for (LevelListener listener : world.listeners) {
-                            listener.allChanged(false, true);
-                        }
+                        world.allChanged(false, true);
                         source.sendTranslatableMessage("command.commands.chunk.reset.success", x, z);
                         return Command.SINGLE_SUCCESS;
                     }))));
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandPlace.java b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandPlace.java
index 7ea83be8e..f6d5be8e9 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandPlace.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandPlace.java
@@ -39,9 +39,7 @@ public class CommandPlace implements CommandManager.CommandRegistry {
 
                                     boolean success = feature.place(world, source.getWorld().rand, x, y, z);
                                     if (success) {
-                                        for (LevelListener listener : world.listeners) {
-                                            listener.allChanged(true, false);
-                                        }
+                                        world.allChanged(true, false);
                                         source.sendTranslatableMessage("command.commands.place.success", feature.getClass().getSimpleName().substring(12), x, y, z);
                                     } else {
                                         throw FAILURE.create();
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandTime.java b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandTime.java
index d996bc1d2..384fe00e3 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandTime.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandTime.java
@@ -134,9 +134,7 @@ public class CommandTime implements CommandManager.CommandRegistry {
 
     private static void setWorldTime(CommandSource source, World world, long time) {
         world.setWorldTime(time);
-        for (LevelListener listener : world.listeners) {
-            listener.allChanged(true, true);
-        }
+        world.allChanged(true, true);
         source.sendTranslatableMessage("command.commands.time.set", time);
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandWeather.java b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandWeather.java
index bfea50f68..469daab44 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandWeather.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandWeather.java
@@ -24,7 +24,7 @@ public class CommandWeather implements CommandManager.CommandRegistry {
                         World world = source.getWorld();
                         Weather weather = Weathers.OVERWORLD_CLEAR;
 
-                        world.weatherManager.overrideWeather(weather);
+                        world.getWeatherManager().overrideWeather(weather);
                         source.sendTranslatableMessage("command.commands.weather.success", weather.getNameLanguageKey());
                         return Command.SINGLE_SUCCESS;
                     }))
@@ -34,7 +34,7 @@ public class CommandWeather implements CommandManager.CommandRegistry {
                             World world = source.getWorld();
                             Weather weather = c.getArgument("weather", Weather.class);
 
-                            world.weatherManager.overrideWeather(weather);
+                            world.getWeatherManager().overrideWeather(weather);
                             source.sendTranslatableMessage("command.commands.weather.success", weather.getNameLanguageKey());
                             return Command.SINGLE_SUCCESS;
                         })
@@ -44,7 +44,7 @@ public class CommandWeather implements CommandManager.CommandRegistry {
                                     World world = source.getWorld();
                                     Weather weather = c.getArgument("weather", Weather.class);
 
-                                    world.weatherManager.overrideWeather(weather, c.getArgument("duration", Integer.class));
+                                    world.getWeatherManager().overrideWeather(weather, c.getArgument("duration", Integer.class));
                                     source.sendTranslatableMessage("command.commands.weather.success", weather.getNameLanguageKey());
                                     return Command.SINGLE_SUCCESS;
                                 })
@@ -54,7 +54,7 @@ public class CommandWeather implements CommandManager.CommandRegistry {
                                             World world = source.getWorld();
                                             Weather weather = c.getArgument("weather", Weather.class);
 
-                                            world.weatherManager.overrideWeather(weather, c.getArgument("duration", Integer.class), c.getArgument("power", Float.class));
+                                            world.getWeatherManager().overrideWeather(weather, c.getArgument("duration", Integer.class), c.getArgument("power", Float.class));
                                             source.sendTranslatableMessage("command.commands.weather.success", weather.getNameLanguageKey());
                                             return Command.SINGLE_SUCCESS;
                                         })))));
diff --git a/game/core/src/main/java/net/minecraft/core/world/BlocksContainer.java b/game/core/src/main/java/net/minecraft/core/world/BlocksContainer.java
index 877bf8593..4b8e0a4b8 100644
--- a/game/core/src/main/java/net/minecraft/core/world/BlocksContainer.java
+++ b/game/core/src/main/java/net/minecraft/core/world/BlocksContainer.java
@@ -11,6 +11,7 @@ import net.minecraft.core.world.biome.Biome;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.pos.TilePosc;
 import net.minecraft.core.world.season.SeasonManager;
+import net.minecraft.core.world.weather.WeatherManager;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -203,6 +204,11 @@ public class BlocksContainer implements MutableWorldSource {
         return this.world.getSeasonManager();
     }
 
+    @Override
+    public @NotNull WeatherManager getWeatherManager() {
+        return this.world.getWeatherManager();
+    }
+
     @Override
     public @NotNull Biome getBlockBiome(final @NotNull TilePosc tilePos) {
         return this.world.getBlockBiome(tilePos);
diff --git a/game/core/src/main/java/net/minecraft/core/world/World.java b/game/core/src/main/java/net/minecraft/core/world/World.java
index 13fda2270..f511aaa17 100644
--- a/game/core/src/main/java/net/minecraft/core/world/World.java
+++ b/game/core/src/main/java/net/minecraft/core/world/World.java
@@ -96,17 +96,27 @@ public abstract class World implements MutableWorldSource {
 
     public final @NotNull List<@NotNull Entity> entities = new ObjectArrayList<>();
     private final @NotNull List<@NotNull Entity> entitiesToRemove = new ObjectArrayList<>();
+
     private final @NotNull TreeSet<@NotNull NextTickListEntry> tickNextTickList = new TreeSet<>();
     private final @NotNull Set<@NotNull NextTickListEntry> tickNextTickSet = new HashSet<>();
+
     public final @NotNull List<@NotNull TileEntity> tileEntityList = new ObjectArrayList<>();
+
     public final @NotNull List<@NotNull Player> players = new ObjectArrayList<>();
+
     public final @NotNull List<@NotNull Entity> weatherEffects = new ObjectArrayList<>();
+
     public final @NotNull List<@NotNull AABBdc> collidingBoundingBoxes = new ObjectArrayList<>();
-    public final @NotNull List<@NotNull LevelListener> listeners = new ObjectArrayList<>();
+
+    private final @NotNull List<@NotNull LevelListener> listeners = new ObjectArrayList<>();
     private final @NotNull Set<@NotNull ChunkPosc> positionsToUpdate = new ObjectOpenHashSet<>();
     private final @NotNull List<@NotNull Entity> entityBuffer = new ObjectArrayList<>();
     private final @NotNull Set<@NotNull NextTickListEntry> immediatelyUpdatedPositions = new ObjectOpenHashSet<>();
 
+    public final @NotNull Random rand = new Random();
+    public final @NotNull Dimension dimension;
+    public final boolean isNewWorld;
+
     public boolean scheduledUpdatesAreImmediate = false;
     public int skyDarken = 0;
     protected int updateLCG = new Random().nextInt();
@@ -116,16 +126,8 @@ public abstract class World implements MutableWorldSource {
     public boolean dayCanHaveRainbow = true;
     public boolean noNeighborUpdate = false;
 
-    public final @NotNull Random rand = new Random();
-    public final boolean isNewWorld;
-    public final @NotNull Dimension dimension;
-    public final @NotNull ChunkProvider chunkProvider;
-    private final @NotNull LevelStorage levelStorage;
-    private final @NotNull LevelData levelData;
-    private final @NotNull DimensionData dimensionData;
     public boolean findingSpawnPoint;
     protected boolean enoughPlayersSleeping;
-    protected final @NotNull LightingEngine lightingEngine = new LightingEngineLegacy(this);
     private boolean updatingTileEntities;
 
     private int caveSoundCounter = this.rand.nextInt(12000);
@@ -134,12 +136,16 @@ public abstract class World implements MutableWorldSource {
     private long runtime = 0;
     public int dayCountLastTick;
 
-    public final @NotNull BiomeProvider biomeProvider;
-    public final @NotNull SeasonManager seasonManager;
-    public WeatherManager weatherManager;
-
+    private final @NotNull ChunkProvider chunkProvider;
+    private final @NotNull LevelStorage levelStorage;
+    private final @NotNull LevelData levelData;
+    private final @NotNull DimensionData dimensionData;
+    private final @NotNull BiomeProvider biomeProvider;
+    private final @NotNull SeasonManager seasonManager;
+    private final @NotNull WeatherManager weatherManager;
     private final @NotNull CommandManager commandManager = new CommandManager(isServer);
     private final @NotNull WireHandler wireHandler = new WireHandler(this);
+    private final @NotNull LightingEngine lightingEngine = new LightingEngineLegacy(this);
 
     private World(
         final long seed,
@@ -334,7 +340,7 @@ public abstract class World implements MutableWorldSource {
     }
 
     public @Nullable Weather getCurrentWeather() {
-        return (this.weatherManager == null) ? null : this.weatherManager.getCurrentWeather();
+        return this.weatherManager.getCurrentWeather();
     }
 
     @Override
@@ -403,6 +409,10 @@ public abstract class World implements MutableWorldSource {
         return this.seasonManager;
     }
 
+    public @NotNull WeatherManager getWeatherManager() {
+        return this.weatherManager;
+    }
+
     @Override
     public double getBlockVariety(final @NotNull TilePosc tilePos) {
         if (tilePos.x() < MIN_BLOCK_X || tilePos.z() < MIN_BLOCK_Z || tilePos.x() > MAX_BLOCK_X || tilePos.z() > MAX_BLOCK_Z) {
@@ -911,7 +921,6 @@ public abstract class World implements MutableWorldSource {
         return this.checkBlockCollisionBetweenPoints(start, end, false, false, false);
     }
 
-
     public @Nullable HitResult checkBlockCollisionBetweenPoints(@NotNull final Vector3dc start, @NotNull final Vector3dc end, final boolean shouldCollideWithFluids) {
         return this.checkBlockCollisionBetweenPoints(start, end, shouldCollideWithFluids, false, false);
     }
@@ -1287,15 +1296,15 @@ public abstract class World implements MutableWorldSource {
         float bright = 1.0F - (MathHelper.cos(angle * MathHelper.PI * 2.0F) * 2.0F + 0.2F);
         bright = 1.0f - MathHelper.clamp(bright, 0.0f, 1.0f);
 
-        final Weather weather = this.weatherManager.getCurrentWeather();
+        final Weather weather = this.getWeatherManager().getCurrentWeather();
         if (weather != null) {
             // TODO Unhardcode somehow
             if (weather == Weathers.OVERWORLD_RAIN) {
-                final float power = this.weatherManager.getWeatherIntensity() * this.weatherManager.getWeatherPower();
+                final float power = this.getWeatherManager().getWeatherIntensity() * this.getWeatherManager().getWeatherPower();
                 bright = (float) (bright * (1.0 - (power * 5.0f) / 16.0));
             }
             if (weather == Weathers.OVERWORLD_STORM) {
-                final float power = this.weatherManager.getWeatherIntensity() * this.weatherManager.getWeatherPower();
+                final float power = this.getWeatherManager().getWeatherIntensity() * this.getWeatherManager().getWeatherPower();
                 bright = (float) (bright * (1.0 - (power * 5.0f) / 16.0));
                 bright = (float) (bright * (1.0 - (power * 5.0f) / 16.0));
             }
@@ -1954,7 +1963,7 @@ public abstract class World implements MutableWorldSource {
         Debug.push("misc");
         this.immediatelyUpdatedPositions.clear();
         Debug.change("weather");
-        this.weatherManager.tick();
+        this.getWeatherManager().tick();
         Debug.change("misc");
         this.updateSleepingPlayers();
         Debug.change("spawning");
@@ -2689,7 +2698,7 @@ public abstract class World implements MutableWorldSource {
         }
         final @Nullable Weather currentWeather = this.getCurrentWeather();
         if (currentWeather instanceof IPrecipitation && ((IPrecipitation) currentWeather).spawnRainParticles()) {
-            this.weatherManager.overrideWeather(this.getWorldType().getDefaultWeather());
+            this.getWeatherManager().overrideWeather(this.getWorldType().getDefaultWeather());
         }
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/world/WorldSource.java b/game/core/src/main/java/net/minecraft/core/world/WorldSource.java
index bfea39716..fc1ca427e 100644
--- a/game/core/src/main/java/net/minecraft/core/world/WorldSource.java
+++ b/game/core/src/main/java/net/minecraft/core/world/WorldSource.java
@@ -9,6 +9,7 @@ import net.minecraft.core.world.biome.Biome;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.pos.TilePosc;
 import net.minecraft.core.world.season.SeasonManager;
+import net.minecraft.core.world.weather.WeatherManager;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -121,6 +122,8 @@ public interface WorldSource {
 
     @NotNull SeasonManager getSeasonManager();
 
+    @NotNull WeatherManager getWeatherManager();
+
     @NotNull Biome getBlockBiome(final @NotNull TilePosc tilePos);
 
     @Deprecated
diff --git a/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkCache.java b/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkCache.java
index 3591baf06..f09a4fa87 100644
--- a/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkCache.java
+++ b/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkCache.java
@@ -15,6 +15,7 @@ import net.minecraft.core.world.pos.ChunkTilePos;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.pos.TilePosc;
 import net.minecraft.core.world.season.SeasonManager;
+import net.minecraft.core.world.weather.WeatherManager;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -219,6 +220,11 @@ public class ChunkCache
         return this.world.getSeasonManager();
     }
 
+    @Override
+    public @NotNull WeatherManager getWeatherManager() {
+        return this.world.getWeatherManager();
+    }
+
     @Override
     public @NotNull Biome getBlockBiome(final @NotNull TilePosc tilePos) {
         return this.world.getBlockBiome(tilePos);
diff --git a/game/core/src/main/java/net/minecraft/core/world/type/WorldTypeEmpty.java b/game/core/src/main/java/net/minecraft/core/world/type/WorldTypeEmpty.java
index 208d5afb3..514d28a5b 100644
--- a/game/core/src/main/java/net/minecraft/core/world/type/WorldTypeEmpty.java
+++ b/game/core/src/main/java/net/minecraft/core/world/type/WorldTypeEmpty.java
@@ -76,7 +76,7 @@ public class WorldTypeEmpty
         float weatherOffset = 0;
         Weather currentWeather = world.getCurrentWeather();
         if (currentWeather != null)
-            weatherOffset = currentWeather.getLightLevelSubtracted() * world.weatherManager.getWeatherIntensity() * world.weatherManager.getWeatherPower();
+            weatherOffset = currentWeather.getLightLevelSubtracted() * world.getWeatherManager().getWeatherIntensity() * world.getWeatherManager().getWeatherPower();
         return (int) (f2 * (11F - weatherOffset) + weatherOffset);
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/world/type/drift/WorldTypeDrift.java b/game/core/src/main/java/net/minecraft/core/world/type/drift/WorldTypeDrift.java
index 322629de5..72ff79c14 100644
--- a/game/core/src/main/java/net/minecraft/core/world/type/drift/WorldTypeDrift.java
+++ b/game/core/src/main/java/net/minecraft/core/world/type/drift/WorldTypeDrift.java
@@ -94,7 +94,7 @@ public class WorldTypeDrift
         }
         float weatherOffset = 0;
         if (world.getCurrentWeather() != null)
-            weatherOffset = world.getCurrentWeather().getLightLevelSubtracted() * world.weatherManager.getWeatherIntensity() * world.weatherManager.getWeatherPower();
+            weatherOffset = world.getCurrentWeather().getLightLevelSubtracted() * world.getWeatherManager().getWeatherIntensity() * world.getWeatherManager().getWeatherPower();
         int subtracted = (int) (f2 * (11F - weatherOffset) + weatherOffset);
         if (subtracted > 8) subtracted = 8;
         return subtracted;
diff --git a/game/core/src/main/java/net/minecraft/core/world/type/overworld/WorldTypeOverworld.java b/game/core/src/main/java/net/minecraft/core/world/type/overworld/WorldTypeOverworld.java
index a0291dc1d..d05a103d0 100644
--- a/game/core/src/main/java/net/minecraft/core/world/type/overworld/WorldTypeOverworld.java
+++ b/game/core/src/main/java/net/minecraft/core/world/type/overworld/WorldTypeOverworld.java
@@ -91,7 +91,7 @@ public class WorldTypeOverworld
         float weatherOffset = 0;
         Weather currentWeather = world.getCurrentWeather();
         if (currentWeather != null)
-            weatherOffset = currentWeather.getLightLevelSubtracted() * world.weatherManager.getWeatherIntensity() * world.weatherManager.getWeatherPower();
+            weatherOffset = currentWeather.getLightLevelSubtracted() * world.getWeatherManager().getWeatherIntensity() * world.getWeatherManager().getWeatherPower();
         return (int) (f2 * (11F - weatherOffset) + weatherOffset);
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/world/weather/WeatherSnow.java b/game/core/src/main/java/net/minecraft/core/world/weather/WeatherSnow.java
index 4326644c0..b506f58d3 100644
--- a/game/core/src/main/java/net/minecraft/core/world/weather/WeatherSnow.java
+++ b/game/core/src/main/java/net/minecraft/core/world/weather/WeatherSnow.java
@@ -42,7 +42,7 @@ public class WeatherSnow
 
     @Override
     public void doChunkLoadEffect(@NotNull World world, @NotNull Chunk chunk) {
-        if (world.weatherManager.getWeatherPower() <= 0.6f) return;
+        if (world.getWeatherManager().getWeatherPower() <= 0.6f) return;
         for (int x = 0; x < 16; x++) {
             int worldX = chunk.pos.x * 16 + x;
             zLoop:
@@ -79,7 +79,7 @@ public class WeatherSnow
 
     @Override
     public void doEnvironmentUpdate(@NotNull World world, @NotNull Random rand, int x, int z) {
-        int probability = (int) (64 * (1 / world.weatherManager.getWeatherPower()));
+        int probability = (int) (64 * (1 / world.getWeatherManager().getWeatherPower()));
         if (world.getSeasonManager().getCurrentSeason() != null && world.getSeasonManager().getCurrentSeason().hasDeeperSnow) {
             probability /= 2;
         }
@@ -96,17 +96,17 @@ public class WeatherSnow
             if (biome.blockedWeathers[i] == this) return;
         }
 
-        if (world.weatherManager.getWeatherPower() > 0.6f && y >= 0 && y < world.getHeightBlocks() && world.getSavedLightValue(LightLayer.Block, x, y, z) < 10) {
+        if (world.getWeatherManager().getWeatherPower() > 0.6f && y >= 0 && y < world.getHeightBlocks() && world.getSavedLightValue(LightLayer.Block, x, y, z) < 10) {
             if (blockId == 0 && blockIdBelow != 0 && Blocks.LAYER_SNOW.canPlaceBlockAt(world, x, y, z) && blockIdBelow != Blocks.ICE.id()) {
                 if (snow) {
                     world.setBlockWithNotify(x, y, z, Blocks.LAYER_SNOW.id());
                 }
-            } else if (world.weatherManager.getWeatherPower() > 0.5f && blockId == Blocks.LAYER_SNOW.id() && world.getSeasonManager().getCurrentSeason() != null && (world.getSeasonManager().getCurrentSeason().hasDeeperSnow || biome == Biomes.OVERWORLD_GLACIER)) {
+            } else if (world.getWeatherManager().getWeatherPower() > 0.5f && blockId == Blocks.LAYER_SNOW.id() && world.getSeasonManager().getCurrentSeason() != null && (world.getSeasonManager().getCurrentSeason().hasDeeperSnow || biome == Biomes.OVERWORLD_GLACIER)) {
                 if (snow) {
                     Blocks.LAYER_SNOW.getLogic().accumulate(world, new TilePos(x, y, z));
                 }
             } else if (blockIdBelow == Blocks.FLUID_WATER_STILL.id() && world.getBlockMetadata(x, y - 1, z) == 0) {
-                if (rand.nextFloat() < world.weatherManager.getWeatherPower() * world.weatherManager.getWeatherIntensity()) {
+                if (rand.nextFloat() < world.getWeatherManager().getWeatherPower() * world.getWeatherManager().getWeatherIntensity()) {
                     for (int i = 0; i < 4; i++) {
                         Direction direction = Direction.horizontalDirections[i];
                         Block<?> block = world.getBlock(x + direction.getOffsetX(), y - 1, z + direction.getOffsetZ());
diff --git a/game/core/src/main/java/net/minecraft/core/world/weather/WeatherStorm.java b/game/core/src/main/java/net/minecraft/core/world/weather/WeatherStorm.java
index d65abcd87..c081905ca 100644
--- a/game/core/src/main/java/net/minecraft/core/world/weather/WeatherStorm.java
+++ b/game/core/src/main/java/net/minecraft/core/world/weather/WeatherStorm.java
@@ -32,7 +32,7 @@ public class WeatherStorm extends WeatherRain {
     public void doEnvironmentUpdate(final @NotNull World world, final @NotNull Random rand, final int x, final int z) {
         super.doEnvironmentUpdate(world, rand, x, z);
 
-        if (rand.nextInt((int) (100000 * (1 / world.weatherManager.getWeatherPower()))) == 0) {
+        if (rand.nextInt((int) (100000 * (1 / world.getWeatherManager().getWeatherPower()))) == 0) {
             final int searchRadius = 32;
 
             int maxWeight = Integer.MIN_VALUE;
diff --git a/game/server/src/main/java/net/minecraft/server/world/WorldServer.java b/game/server/src/main/java/net/minecraft/server/world/WorldServer.java
index a8b0efd4d..a25fbbe06 100644
--- a/game/server/src/main/java/net/minecraft/server/world/WorldServer.java
+++ b/game/server/src/main/java/net/minecraft/server/world/WorldServer.java
@@ -260,7 +260,7 @@ public class WorldServer extends World {
         super.tick();
 
         if (System.currentTimeMillis() - lastWeatherSend > 1000) {
-            mcServer.playerList.sendPacketToAllPlayers(new PacketWeatherStatus(dimension.id, getCurrentWeather() != null ? getCurrentWeather().getId() : -1, weatherManager.getNextWeather() != null ? weatherManager.getNextWeather().getId() : -1, weatherManager.getWeatherDuration(), weatherManager.getWeatherIntensity(), weatherManager.getWeatherPower()));
+            mcServer.playerList.sendPacketToAllPlayers(new PacketWeatherStatus(dimension.id, getCurrentWeather() != null ? getCurrentWeather().getId() : -1, getWeatherManager().getNextWeather() != null ? getWeatherManager().getNextWeather().getId() : -1, getWeatherManager().getWeatherDuration(), getWeatherManager().getWeatherIntensity(), getWeatherManager().getWeatherPower()));
             lastWeatherSend = System.currentTimeMillis();
         }
     }
