Skip to content

Commit

Permalink
implement CloTrackingView
Browse files Browse the repository at this point in the history
  • Loading branch information
CursedFlames committed Feb 22, 2024
1 parent fd34ce4 commit 04d1fc1
Show file tree
Hide file tree
Showing 9 changed files with 393 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
import io.github.notstirred.dasm.api.annotations.selector.FieldSig;
import io.github.notstirred.dasm.api.annotations.selector.MethodSig;
import io.github.notstirred.dasm.api.annotations.selector.Ref;
import io.github.opencubicchunks.cubicchunks.server.level.CloTrackingView;
import io.github.opencubicchunks.cubicchunks.server.level.CubicChunkHolder;
import io.github.opencubicchunks.cubicchunks.server.level.CubicTicketType;
import io.github.opencubicchunks.cubicchunks.server.level.progress.CubicChunkProgressListener;
import io.github.opencubicchunks.cubicchunks.world.level.chunklike.CloAccess;
import io.github.opencubicchunks.cubicchunks.world.level.chunklike.CloPos;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkTrackingView;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.TicketType;
Expand Down Expand Up @@ -90,4 +92,10 @@ public abstract CompletableFuture<Either<CloAccess, ChunkHolder.ChunkLoadingFail

@TypeRedirect(from = @Ref(ChunkProgressListener.class), to = @Ref(CubicChunkProgressListener.class))
interface ChunkProgressListener_to_CubicChunkProgressListener_redirects { }

@TypeRedirect(from = @Ref(ChunkTrackingView.class), to = @Ref(CloTrackingView.class))
interface ChunkTrackingView_to_CloTrackingView_redirects { }

@TypeRedirect(from = @Ref(ChunkTrackingView.Positioned.class), to = @Ref(CloTrackingView.Positioned.class))
interface ChunkTrackingView$Positioned_to_CloTrackingView$Positioned_redirects { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public void cc_broadcastChanges(LevelClo clo) {

@AddTransformToSets(GeneralSet.class) @TransformFromMethod(
value = @MethodSig("addSaveDependency(Ljava/lang/String;Ljava/util/concurrent/CompletableFuture;)V"))
protected native void cc_addSaveDependency(String source, CompletableFuture<?> future);
public native void cc_addSaveDependency(String source, CompletableFuture<?> future);

@AddTransformToSets(GeneralSet.class) @TransformFromMethod(
value = @MethodSig("updateChunkToSave(Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;)V"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public String toString() {
);

for (ChunkHolder holder : cloHolders) {
// holder.addSaveDependency("getChunkRangeFuture " + pos + " " + radius, combinedFuture);
((CubicChunkHolder) holder).cc_addSaveDependency("getChunkRangeFuture " + pos + " " + radius, combinedFuture);
}

cir.setReturnValue(combinedFuture);
Expand Down Expand Up @@ -259,7 +259,6 @@ private boolean cc_updateChunkScheduling_onForgeHook(ServerLevel level, long pos
@AddTransformToSets(GeneralSet.class) @TransformFromMethod(@MethodSig("scheduleChunkGeneration(Lnet/minecraft/server/level/ChunkHolder;Lnet/minecraft/world/level/chunk/ChunkStatus;)Ljava/util/concurrent/CompletableFuture;"))
private native CompletableFuture<Either<CloAccess, ChunkHolder.ChunkLoadingFailure>> cc_scheduleChunkGeneration(ChunkHolder pChunkHolder, ChunkStatus pChunkStatus);

// FIXME requires redirect from ticket type to cubic ticket type
@AddTransformToSets(GeneralSet.class) @TransformFromMethod(@MethodSig("releaseLightTicket(Lnet/minecraft/world/level/ChunkPos;)V"))
protected native void cc_releaseLightTicket(ChunkPos pChunkPos);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.github.opencubicchunks.cubicchunks.mixin.core.common.server.level;

import io.github.opencubicchunks.cubicchunks.server.level.CloTrackingView;
import org.spongepowered.asm.mixin.Mixin;

// Needed for DASM to apply
// TODO won't be necessary once we have dasm.json
@Mixin(CloTrackingView.class)
public interface MixinCloTrackingView {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
package io.github.opencubicchunks.cubicchunks.server.level;

import java.util.function.Consumer;

import io.github.notstirred.dasm.api.annotations.Dasm;
import io.github.notstirred.dasm.api.annotations.redirect.redirects.AddFieldToSets;
import io.github.notstirred.dasm.api.annotations.redirect.redirects.AddMethodToSets;
import io.github.notstirred.dasm.api.annotations.selector.FieldSig;
import io.github.notstirred.dasm.api.annotations.selector.MethodSig;
import io.github.notstirred.dasm.api.annotations.selector.Ref;
import io.github.opencubicchunks.cc_core.api.CubicConstants;
import io.github.opencubicchunks.cc_core.utils.Coords;
import io.github.opencubicchunks.cubicchunks.mixin.GeneralSet;
import io.github.opencubicchunks.cubicchunks.mixin.GlobalSet;
import io.github.opencubicchunks.cubicchunks.world.level.chunklike.CloPos;
import net.minecraft.server.level.ChunkTrackingView;
import net.minecraft.world.level.ChunkPos;

@Dasm(GeneralSet.class)
public interface CloTrackingView extends ChunkTrackingView {
@AddFieldToSets(sets = GlobalSet.class, owner = @Ref(ChunkTrackingView.class), field = @FieldSig(type = @Ref(ChunkTrackingView.class), name = "EMPTY"))
CloTrackingView EMPTY = new CloTrackingView() {
@Override public boolean cc_contains(int x, int y, int z, boolean pSearchAllChunks) {
return false;
}

@Override public void cc_forEach(Consumer<CloPos> pAction) {
}

@Override
public boolean contains(int x, int z, boolean pSearchAllChunks) {
return false;
}

@Override
public void forEach(Consumer<ChunkPos> pAction) {
}
};

@AddMethodToSets(sets = GlobalSet.class, owner = @Ref(ChunkTrackingView.class), method = @MethodSig("of(Lnet/minecraft/world/level/ChunkPos;I)Lnet/minecraft/server/level/ChunkTrackingView;"))
static CloTrackingView cc_of(CloPos pCenter, int pViewDistance) {
return new CloTrackingView.Positioned(pCenter, pViewDistance);
}

@AddMethodToSets(sets = GlobalSet.class, owner = @Ref(ChunkTrackingView.class), method = @MethodSig("difference(Ljava/util/function/Consumer;Ljava/util/function/Consumer;)V"))
static void cc_difference(CloTrackingView pOldCloTrackingView, CloTrackingView pNewCloTrackingView, Consumer<CloPos> pChunkDropper, Consumer<CloPos> pChunkMarker) {
if (!pOldCloTrackingView.equals(pNewCloTrackingView)) {
if (pOldCloTrackingView instanceof Positioned oldPositioned
&& pNewCloTrackingView instanceof Positioned newPositioned) {
if (oldPositioned.cc_cubeIntersects(newPositioned)) {
int minX = Math.min(oldPositioned.minX(), newPositioned.minX());
int minY = Math.min(oldPositioned.minY(), newPositioned.minY());
int minZ = Math.min(oldPositioned.minZ(), newPositioned.minZ());
int maxX = Math.max(oldPositioned.maxX(), newPositioned.maxX());
int maxY = Math.max(oldPositioned.maxY(), newPositioned.maxY());
int maxZ = Math.max(oldPositioned.maxZ(), newPositioned.maxZ());

for(int x = minX; x <= maxX; ++x) {
for (int z = minZ; z <= maxZ; ++z) {
for (int dx = 0; dx < CubicConstants.DIAMETER_IN_SECTIONS; dx++) {
for (int dz = 0; dz < CubicConstants.DIAMETER_IN_SECTIONS; dz++) {
int chunkX = Coords.cubeToSection(x, dx);
int chunkZ = Coords.cubeToSection(z, dz);
boolean oldHas = oldPositioned.contains(chunkX, chunkZ);
boolean newHas = newPositioned.contains(chunkX, chunkZ);
if (oldHas != newHas) {
if (newHas) {
pChunkDropper.accept(CloPos.chunk(chunkX, chunkZ));
} else {
pChunkMarker.accept(CloPos.chunk(chunkX, chunkZ));
}
}
}
}
for (int y = minY; y <= maxY; ++y) {
boolean oldHas = oldPositioned.cc_contains(x, y, z);
boolean newHas = newPositioned.cc_contains(x, y, z);
if (oldHas != newHas) {
if (newHas) {
pChunkDropper.accept(CloPos.cube(x, y, z));
} else {
pChunkMarker.accept(CloPos.cube(x, y, z));
}
}
}
}
}
return;
} else if (oldPositioned.cc_chunkIntersects(newPositioned)) {
int minX = Math.min(oldPositioned.minX(), newPositioned.minX());
int minZ = Math.min(oldPositioned.minZ(), newPositioned.minZ());
int maxX = Math.max(oldPositioned.maxX(), newPositioned.maxX());
int maxZ = Math.max(oldPositioned.maxZ(), newPositioned.maxZ());

for(int x = minX; x <= maxX; ++x) {
for (int z = minZ; z <= maxZ; ++z) {
for (int dx = 0; dx < CubicConstants.DIAMETER_IN_SECTIONS; dx++) {
for (int dz = 0; dz < CubicConstants.DIAMETER_IN_SECTIONS; dz++) {
int chunkX = Coords.cubeToSection(x, dx);
int chunkZ = Coords.cubeToSection(z, dz);
boolean oldHas = oldPositioned.contains(chunkX, chunkZ);
boolean newHas = newPositioned.contains(chunkX, chunkZ);
if (oldHas != newHas) {
if (newHas) {
pChunkDropper.accept(CloPos.chunk(chunkX, chunkZ));
} else {
pChunkMarker.accept(CloPos.chunk(chunkX, chunkZ));
}
}
}
}
}
}

oldPositioned.cc_forEachCubesOnly(pChunkMarker);
newPositioned.cc_forEachCubesOnly(pChunkDropper);
return;
}
}

pOldCloTrackingView.cc_forEach(pChunkMarker);
pNewCloTrackingView.cc_forEach(pChunkDropper);
}
}

@AddMethodToSets(sets = GlobalSet.class, owner = @Ref(ChunkTrackingView.class), method = @MethodSig("contains(Lnet/minecraft/world/level/ChunkPos;)Z"))
default boolean cc_contains(CloPos cloPos) {
if (cloPos.isCube()) {
return this.cc_contains(cloPos.getX(), cloPos.getY(), cloPos.getZ());
} else {
return this.contains(cloPos.getX(), cloPos.getZ());
}
}

default boolean cc_contains(int x, int y, int z) {
return this.cc_contains(x, y, z, true);
}

boolean cc_contains(int x, int y, int z, boolean pSearchAllChunks);

@AddMethodToSets(sets = GlobalSet.class, owner = @Ref(ChunkTrackingView.class), method = @MethodSig("forEach(Ljava/util/function/Consumer;)V"))
void cc_forEach(Consumer<CloPos> pAction);

default boolean cc_isInViewDistance(int x, int y, int z) {
return this.cc_contains(x, y, z, false);
}

static boolean cc_isInViewDistance(int centerX, int centerY, int centerZ, int viewDistance, int x, int y, int z) {
return cc_isWithinDistance(centerX, centerY, centerZ, viewDistance, x, y, z, false);
}

static boolean cc_isWithinDistance(int centerX, int centerY, int centerZ, int viewDistance, int x, int y, int z, boolean increaseRadiusByOne) {
// Mojang does some weird jank, but it's almost identical to just increasing the view distance by 1 - so we do that instead
if (increaseRadiusByOne) viewDistance++;
int dx = Math.max(0, Math.abs(x - centerX) - 1);
int dy = Math.max(0, Math.abs(y - centerY) - 1);
int dz = Math.max(0, Math.abs(z - centerZ) - 1);
return dx*dx + dy*dy + dz*dz < viewDistance * viewDistance;
}

static boolean cc_isWithinDistanceCubeColumn(int centerX, int centerZ, int viewDistance, int x, int z, boolean increaseRadiusByOne) {
return cc_isWithinDistance(centerX, 0, centerZ, viewDistance, x, 0, z, increaseRadiusByOne);
}

record Positioned(CloPos center, int viewDistance) implements CloTrackingView {
int minX() {
return this.center.getX() - this.viewDistance - 1;
}

int minY() {
return this.center.getY() - this.viewDistance - 1;
}

int minZ() {
return this.center.getZ() - this.viewDistance - 1;
}

int maxX() {
return this.center.getX() + this.viewDistance + 1;
}

int maxY() {
return this.center.getY() + this.viewDistance + 1;
}

int maxZ() {
return this.center.getZ() + this.viewDistance + 1;
}

@Override
public boolean contains(int x, int z, boolean searchAllChunks) {
return cc_isWithinDistanceCubeColumn(this.center.getX(), this.center.getZ(), this.viewDistance, Coords.sectionToCube(x), Coords.sectionToCube(z), searchAllChunks);
}

@Override
public void forEach(Consumer<ChunkPos> pAction) {
for(int x = this.minX(); x <= this.maxX(); ++x) {
for(int z = this.minZ(); z <= this.maxZ(); ++z) {
if (this.cc_contains(x, center.getY(), z)) {
for (int dx = 0; dx < CubicConstants.DIAMETER_IN_SECTIONS; dx++) {
for (int dz = 0; dz < CubicConstants.DIAMETER_IN_SECTIONS; dz++) {
pAction.accept(new ChunkPos(Coords.cubeToSection(x, dx), Coords.cubeToSection(z, dz)));
}
}
}
}
}
}

private boolean cc_cubeIntersects(CloTrackingView.Positioned pOther) {
return this.minX() <= pOther.maxX() && this.maxX() >= pOther.minX()
&& this.minY() <= pOther.maxY() && this.maxY() >= pOther.minY()
&& this.minZ() <= pOther.maxZ() && this.maxZ() >= pOther.minZ();
}

private boolean cc_chunkIntersects(CloTrackingView.Positioned pOther) {
return this.minX() <= pOther.maxX() && this.maxX() >= pOther.minX()
&& this.minZ() <= pOther.maxZ() && this.maxZ() >= pOther.minZ();
}

@Override public boolean cc_contains(int x, int y, int z, boolean searchAllChunks) {
return cc_isWithinDistance(this.center.getX(), this.center.getY(), this.center.getZ(), this.viewDistance, x, y, z, searchAllChunks);
}

@Override public void cc_forEach(Consumer<CloPos> pAction) {
for(int x = this.minX(); x <= this.maxX(); ++x) {
for(int z = this.minZ(); z <= this.maxZ(); ++z) {
if (this.cc_contains(x, center.getY(), z)) {
for (int dx = 0; dx < CubicConstants.DIAMETER_IN_SECTIONS; dx++) {
for (int dz = 0; dz < CubicConstants.DIAMETER_IN_SECTIONS; dz++) {
pAction.accept(CloPos.chunk(Coords.cubeToSection(x, dx), Coords.cubeToSection(z, dz)));
}
}
}
for(int y = this.minY(); y <= this.maxY(); ++y) {
if (this.cc_contains(x, y, z)) {
pAction.accept(CloPos.cube(x, y, z));
}
}
}
}
}

private void cc_forEachCubesOnly(Consumer<CloPos> pAction) {
for(int x = this.minX(); x <= this.maxX(); ++x) {
for(int z = this.minZ(); z <= this.maxZ(); ++z) {
for(int y = this.minY(); y <= this.maxY(); ++y) {
if (this.cc_contains(x, y, z)) {
pAction.accept(CloPos.cube(x, y, z));
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public interface CubicChunkHolder {

CompletableFuture<Either<CloAccess, ChunkHolder.ChunkLoadingFailure>> cc_getOrScheduleFuture(ChunkStatus status, ChunkMap map);

void cc_addSaveDependency(String source, CompletableFuture<?> future);

@FunctionalInterface
interface LevelChangeListener {
void onLevelChange(CloPos cloPos, IntSupplier p_140120_, int p_140121_, IntConsumer p_140122_);
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/cubicchunks.mixins.core.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"common.server.level.MixinChunkTaskPriorityQueueSorter",
"common.server.level.MixinChunkTicketTracker",
"common.server.level.MixinChunkTracker",
"common.server.level.MixinCloTrackingView",
"common.server.level.MixinDistanceManager",
"common.server.level.MixinFixedPlayerDistanceChunkTracker",
"common.server.level.MixinPlayerRespawnLogic",
Expand Down
Loading

0 comments on commit 04d1fc1

Please sign in to comment.