Skip to content

Commit

Permalink
Fix chunk flicker with Sodium 0.3.0, release version 2.0.4
Browse files Browse the repository at this point in the history
They removed the buffering behavior of their ChunkStatusListener which was
present in snapshot versions of 0.3.0, so we need to take care of that manually
again.
  • Loading branch information
Johni0702 committed Jul 18, 2021
1 parent a254d30 commit 9fb17d3
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 2.0.4
- Fix chunk flicker with Sodium 0.3.0 (broke shortly before release)

### 2.0.3
- Update to Minecraft 1.17.1

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ org.gradle.jvmargs=-Xmx2G
loaderVersion=0.11.6

# Mod Properties
modVersion = 2.0.3
modVersion = 2.0.4
mavenGroup = de.johni0702.minecraft
archivesBaseName = bobby
github.owner = johni0702
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
package de.johni0702.minecraft.bobby.ext;

import de.johni0702.minecraft.bobby.FakeChunkManager;
import me.jellysquid.mods.sodium.client.world.ChunkStatusListener;

public interface ClientChunkManagerExt {
FakeChunkManager bobby_getFakeChunkManager();
void bobby_onFakeChunkAdded(int x, int z);
void bobby_onFakeChunkRemoved(int x, int z);

/**
* Mark Sodium's {@link ChunkStatusListener} as paused.
* This effectively delays all unload notifications until un-paused and most importantly removes redundant (as in
* unload followed by a load) ones. Otherwise Sodium will unload the geometry and the chunk will be missing until
* it is rebuilt.
*
* Has no effect on the vanilla renderer because it polls chunks every frame and gets no intermediate state.
*
* This method is idempotent.
*/
void bobby_pauseChunkStatusListener();

/**
* Resumes Sodium's {@link ChunkStatusListener}, forwarding all updates which are still applicable.
*/
void bobby_resumeChunkStatusListener();
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,18 @@ private void bobbyUnloadFakeChunk(int x, int z, BiomeArray biomes, PacketByteBuf
return;
}

bobby_pauseChunkStatusListener();

// This needs to be called unconditionally because even if there is no chunk loaded at the moment,
// we might already have one queued which we need to cancel as otherwise it will overwrite the real one later.
bobbyChunkManager.unload(x, z, true);
}

@Inject(method = "loadChunkFromPacket", at = @At("RETURN"))
private void bobbyPostLoadRealChunk(CallbackInfoReturnable<WorldChunk> cir) {
bobby_resumeChunkStatusListener();
}

@Unique
private void saveRealChunk(long chunkPos) {
int chunkX = ChunkPos.getPackedX(chunkPos);
Expand All @@ -105,6 +112,8 @@ private void saveRealChunk(long chunkPos) {

if (bobbyChunkManager.shouldBeLoaded(chunkX, chunkZ)) {
bobbyChunkReplacements.add(Pair.of(chunkPos, tag));

bobby_pauseChunkStatusListener();
}
}

Expand All @@ -117,6 +126,8 @@ private void substituteFakeChunksForUnloadedRealOnes() {
bobbyChunkManager.load(chunkX, chunkZ, entry.getValue(), bobbyChunkManager.getStorage());
}
bobbyChunkReplacements.clear();

bobby_resumeChunkStatusListener();
}

@Inject(method = "unload", at = @At("HEAD"))
Expand Down Expand Up @@ -173,4 +184,14 @@ public void bobby_onFakeChunkAdded(int x, int z) {
public void bobby_onFakeChunkRemoved(int x, int z) {
// Vanilla polls for chunks each frame, this is only of interest for Sodium (see SodiumChunkManagerMixin)
}

@Override
public void bobby_pauseChunkStatusListener() {
// Vanilla polls for chunks each frame, this is only of interest for Sodium (see SodiumChunkManagerMixin)
}

@Override
public void bobby_resumeChunkStatusListener() {
// Vanilla polls for chunks each frame, this is only of interest for Sodium (see SodiumChunkManagerMixin)
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package de.johni0702.minecraft.bobby.mixin.sodium;

import de.johni0702.minecraft.bobby.ext.ClientChunkManagerExt;
import de.johni0702.minecraft.bobby.sodium.BufferedChunkStatusListener;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import me.jellysquid.mods.sodium.client.world.ChunkStatusListener;
import net.minecraft.client.world.ClientChunkManager;
import net.minecraft.util.math.ChunkPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;

@Mixin(value = ClientChunkManager.class, priority = 1010) // higher than our normal one
public abstract class SodiumChunkManagerMixin implements ClientChunkManagerExt {
Expand All @@ -14,6 +16,9 @@ public abstract class SodiumChunkManagerMixin implements ClientChunkManagerExt {
/* Shadows the one in Sodium's Mixin */
private ChunkStatusListener listener;

@Unique
private BufferedChunkStatusListener bufferedListener;

@Override
public void bobby_onFakeChunkAdded(int x, int z) {
if (this.listener != null && this.loadedChunks.add(ChunkPos.toLong(x, z))) {
Expand All @@ -27,4 +32,27 @@ public void bobby_onFakeChunkRemoved(int x, int z) {
this.listener.onChunkRemoved(x, z);
}
}

@Override
public void bobby_pauseChunkStatusListener() {
if (this.listener == this.bufferedListener) {
return;
}

if (this.bufferedListener == null || this.bufferedListener.delegate != this.listener) {
this.bufferedListener = new BufferedChunkStatusListener(this.listener);
}

this.listener = this.bufferedListener;
}

@Override
public void bobby_resumeChunkStatusListener() {
if (this.listener != this.bufferedListener) {
return;
}

this.bufferedListener.flush();
this.listener = this.bufferedListener.delegate;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package de.johni0702.minecraft.bobby.sodium;

import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import me.jellysquid.mods.sodium.client.world.ChunkStatusListener;
import net.minecraft.util.math.ChunkPos;

public class BufferedChunkStatusListener implements ChunkStatusListener {
public final ChunkStatusListener delegate;
private final LongSet unloaded = new LongOpenHashSet();

public BufferedChunkStatusListener(ChunkStatusListener delegate) {
this.delegate = delegate;
}

@Override
public void onChunkAdded(int x, int z) {
if (!this.unloaded.remove(ChunkPos.toLong(x, z))) {
this.delegate.onChunkAdded(x, z);
}
}

@Override
public void onChunkRemoved(int x, int z) {
this.unloaded.add(ChunkPos.toLong(x, z));
}

public void flush() {
for (long pos : this.unloaded) {
this.delegate.onChunkRemoved(ChunkPos.getPackedX(pos), ChunkPos.getPackedZ(pos));
}
this.unloaded.clear();
}
}

0 comments on commit 9fb17d3

Please sign in to comment.