Skip to content
This repository has been archived by the owner on Oct 4, 2024. It is now read-only.

Documenting and adding tests to CubicTickingTracker and CubicChunkTracker #130

Merged
merged 3 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minecraft.server.level.ChunkTracker;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.lighting.DynamicGraphMinFixedPoint;
import org.spongepowered.asm.mixin.Mixin;
Expand All @@ -19,23 +20,32 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

/**
* {@link ChunkTracker} is a class that determines the edges and as well as the values to be propagated to the edges in {@link DynamicGraphMinFixedPoint}.
* It marks all chunks in a 1 chunk radius around the center as edges. Edge chunks have 1 level higher than the center chunk.
* <br><br>
* {@link DistanceManager.ChunkTicketTracker} would be the equivalent to {@link net.minecraft.server.level.TickingTracker}. Since that
* implementation does not use {@link ChunkPos} directly, we do not need to mixin it unlike {@link net.minecraft.server.level.TickingTracker}.
* <br><br>
* This mixin replaces {@link ChunkPos} with {@link CloPos} and adds in logic to handle propagation for cubes.
*/
@Mixin(ChunkTracker.class)
public abstract class MixinChunkTracker extends DynamicGraphMinFixedPoint implements MarkableAsCubic {
protected boolean cc_isCubic;
private int cc_noChunkLevel;

/** This is a list of all loaded cubes grouped by "big" column positions (32x32 columns) since cubes are 32x32x32.
* It is needed for quick lookups to determine which cubes are next to a column when propagating level. */
private Long2ObjectMap<IntSet> cc_existingCubesForCubeColumns;

protected MixinChunkTracker() {
super(0, 0, 0);
}

// TODO decide upon a standard way of marking objects as cubic
@Override
public void cc_setCubic() {
cc_isCubic = true;
cc_existingCubesForCubeColumns = new Long2ObjectLinkedOpenHashMap<>();
// TODO do we ever need this to be anything else? in the original CloTracker impl there was a constructor that allowed setting noChunkLevel separately
cc_noChunkLevel = levelCount-1;
}

Expand All @@ -60,6 +70,15 @@ private void cc_onCheckNeighborsAfterUpdate(long pos, int level, boolean isDecre
}
}

/**
* This computes the propagation of the level from neighbor to neighbor.
* <br><br>
* We have to handle two cases: whether we are propagating to a column or a cube.
* <br><br>
* However, it is important to note that you cannot propagate from a column to a cube. This is because
* if we did do that, then a single column could potentially load an infinite amount of cubes. So only
* cubes can propagate to a column, or cubes propagating to cubes.
*/
@Inject(method = "getComputedLevel", at=@At("HEAD"), cancellable = true)
private void cc_onGetComputedLevel(long pos, long excludedSourcePos, int level, CallbackInfoReturnable<Integer> cir) {
if (!cc_isCubic) return;
Expand Down Expand Up @@ -88,6 +107,7 @@ private void cc_onGetComputedLevel(long pos, long excludedSourcePos, int level,
}
}
}
// This is propagating the level from neighboring cubes to this column.
IntSet neighborCubeYSet = this.cc_existingCubesForCubeColumns.get(CloPos.setRawY(pos, 0));
if (neighborCubeYSet != null) {
for (Integer cubeY : neighborCubeYSet) {
Expand Down Expand Up @@ -139,7 +159,12 @@ private void cc_onGetComputedLevel(long pos, long excludedSourcePos, int level,
}
}

// Should be called from each onSetLevel implementation (ChunkTracker is abstract and does not implement it)
/**
* This function adds in new cubes and sorts them into the cube column map.
* <br><br>
* It should be called from each onSetLevel implementation.
* For CC, this should only be {@link MixinTickingTracker#cc_onSetLevel(long, int)} and {@link MixinChunkTracker#cc_onSetLevel(long, int)}.
*/
protected void cc_onSetLevel(long pos, int level) {
if (cc_isCubic && CloPos.isCube(pos)) {
long key = CloPos.setRawY(pos, 0);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package io.github.opencubicchunks.cubicchunks.mixin.core.common.server.level;

import com.llamalad7.mixinextras.injector.WrapWithCondition;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import io.github.opencubicchunks.cubicchunks.mixin.DasmRedirect;
import io.github.opencubicchunks.cubicchunks.mixin.TransformFrom;
import io.github.opencubicchunks.cubicchunks.server.level.CubicTicketType;
import io.github.opencubicchunks.cubicchunks.server.level.CubicTickingTracker;
import io.github.opencubicchunks.cubicchunks.world.level.chunklike.CloPos;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.TicketType;
import net.minecraft.server.level.TickingTracker;
import net.minecraft.world.level.ChunkPos;
Expand All @@ -14,20 +18,37 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@DasmRedirect
/**
* {@link TickingTracker} is a class that manages a Long2ByteMap {@link TickingTracker#chunks} that contains the list of chunks that are ticking.
* This is for the purposes of separating simulation distance from render distance.
* <br><br>
* {@link TickingTracker#chunks} is indexed by ChunkPos, and the value is the level of the chunk.
* The meaning of the ChunkLevel is shown by {@link net.minecraft.server.level.ChunkLevel#byStatus(FullChunkStatus)}.
* The ByteMap only contains values that are below FULL status (level 33), since those are the only ones that need ticking.
* <br><br>
* {@link TickingTracker} also contains a Long2ObjectOpenHashMap {@link TickingTracker#tickets} which contains the actual list of tickets.
* The values from it get propagated to {@link TickingTracker#chunks} in {@link net.minecraft.server.level.ChunkTracker#computeLevelFromNeighbor}.
* <br><br>
* This mixin is used to convert {@link TickingTracker} to use {@link CloPos} instead of {@link ChunkPos}.
*/
@DasmRedirect()
@Mixin(TickingTracker.class)
public abstract class MixinTickingTracker extends MixinChunkTracker implements CubicTickingTracker {
@Inject(method = "setLevel", at = @At("HEAD"))
private void cc_onSetLevel(long pos, int level, CallbackInfo ci) {
super.cc_onSetLevel(pos, level);
}

// TODO we don't want assertion mixins like this outside of dev - put them in mixin.debug instead?
@Inject(method = "getLevel(Lnet/minecraft/world/level/ChunkPos;)I", at = @At("HEAD"))
private void cc_onChunkGetLevel(ChunkPos p_184162_, CallbackInfoReturnable<Integer> cir) {
assert !cc_isCubic;
// TODO: Add this assert back in once we can run DASM before Mixin
/*assert !cc_isCubic;*/
}

/**
* This mixin reverts the creation of a {@link ChunkPos}, then reads the {@link CloPos} from the raw long.
* We know if we are a Chunk or Cube based on {@link CloPos#CLO_Y_COLUMN_INDICATOR}, so there is no ambiguity here.
*/
@WrapWithCondition(method = "replacePlayerTicketsLevel", at = @At(value = "INVOKE",
target = "Lnet/minecraft/server/level/TickingTracker;addTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"))
private <T> boolean cc_onReplacePlayerTicketsLevel(TickingTracker instance, TicketType<T> ticketType, ChunkPos pos, int a, T b) {
Expand All @@ -38,9 +59,21 @@ private <T> boolean cc_onReplacePlayerTicketsLevel(TickingTracker instance, Tick
return false;
}

/**
* We need to replace the reference to {@link TicketType#PLAYER} with {@link CubicTicketType#PLAYER} in {@link TickingTracker#replacePlayerTicketsLevel(int)}.
*/
@WrapOperation(method = "replacePlayerTicketsLevel", at = @At(value = "FIELD", target = "Lnet/minecraft/server/level/TicketType;PLAYER:Lnet/minecraft/server/level/TicketType;"))
private TicketType<?> cc_replaceTicketType(Operation<TicketType<ChunkPos>> original) {
if(!cc_isCubic) return original.call();
return CubicTicketType.PLAYER;
}

@TransformFrom("addTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V")
public abstract <T> void addTicket(TicketType<T> p_184155_, CloPos p_184156_, int p_184157_, T p_184158_);

@TransformFrom("removeTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V")
public abstract <T> void removeTicket(TicketType<T> p_184169_, CloPos p_184170_, int p_184171_, T p_184172_);

@TransformFrom("getLevel(Lnet/minecraft/world/level/ChunkPos;)I")
public abstract int getLevel(CloPos p_184162_);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ public interface CubicTickingTracker {
<T> void addTicket(TicketType<T> p_184155_, CloPos p_184156_, int p_184157_, T p_184158_);

<T> void removeTicket(TicketType<T> p_184169_, CloPos p_184170_, int p_184171_, T p_184172_);

int getLevel(CloPos p_184162_);
}
Loading
Loading