Optimize world generation

This commit is contained in:
ms2k 2026-11-09 22:08:00 +09:00
commit 124e20662c
75 changed files with 3062 additions and 9 deletions

View file

@ -32,10 +32,22 @@ sourceSets {
}
}
configurations { c2me }
def dfcJar = providers.provider {
zipTree(configurations.c2me.singleFile).matching {
include 'META-INF/jars/*-opts-dfc-*.jar'
}.files
}
dependencies {
c2me('curse.maven:c2me-533097:9344813') { transitive = false }
compileOnly files(dfcJar)
testImplementation files(dfcJar)
compileOnly('mekanism:Mekanism:1.21.1-10.7.19.85') { transitive = false }
compileOnly('curse.maven:functional-storage-556861:8459097') { transitive = false }
compileOnly('maven.modrinth:refined-storage:lHHiI26k') { transitive = false }
compileOnly('maven.modrinth:distanthorizons:9w34y8ai') { transitive = false }
compileOnly 'org.jetbrains:annotations:24.1.0'
compileOnly 'org.apiguardian:apiguardian-api:1.1.2'
compileOnly 'io.github.llamalad7:mixinextras-neoforge:0.5.3'

View file

@ -0,0 +1,140 @@
package dev.quorate.tune;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.function.Function;
public final class BiomeIndex {
// Seven squared axis distances cannot overflow a long within this range.
private static final long LIMIT = 1L << 28;
public record Result(Object first, long distance, Object second, long other) {}
private final Object[] nodes;
private final Object[] keys;
private final long[] bounds;
private final int[] next;
private final boolean[] branches;
private final IdentityHashMap<Object, Integer> positions = new IdentityHashMap<>();
private final Object nullKey;
private final boolean narrow;
public BiomeIndex(
Object root,
Function<Object, Object[]> children,
Function<Object, long[]> space,
Function<Object, Object> key) {
var order = new ArrayList<Object>();
var ends = new ArrayList<Integer>();
var inner = new ArrayList<Boolean>();
flatten(root, children, order, ends, inner);
nodes = order.toArray();
keys = new Object[nodes.length];
bounds = new long[nodes.length * 14];
next = new int[nodes.length];
branches = new boolean[nodes.length];
nullKey = key.apply(null);
boolean fits = true;
for (int i = 0; i < nodes.length; i++) {
positions.put(nodes[i], i);
long[] extent = space.apply(nodes[i]);
for (int axis = 0; axis < 7; axis++) {
long low = extent[axis * 2];
long high = extent[axis * 2 + 1];
bounds[i * 14 + axis * 2] = low;
bounds[i * 14 + axis * 2 + 1] = high;
fits &= low >= -LIMIT && low <= LIMIT && high >= -LIMIT && high <= LIMIT;
}
next[i] = ends.get(i);
branches[i] = inner.get(i);
if (!branches[i]) keys[i] = key.apply(nodes[i]);
}
narrow = fits;
}
private static void flatten(
Object node,
Function<Object, Object[]> children,
ArrayList<Object> order,
ArrayList<Integer> ends,
ArrayList<Boolean> inner) {
int index = order.size();
var below = children.apply(node);
order.add(node);
ends.add(0);
inner.add(below != null);
if (below != null) for (var child : below) flatten(child, children, order, ends, inner);
ends.set(index, order.size());
}
public boolean single() {
return !branches[0];
}
private long distance(int index, long[] point, long limit, boolean capped) {
if (index < 0) return Long.MAX_VALUE;
long sum = 0;
int offset = index * 14;
for (int axis = 0; axis < 7; axis++) {
long coordinate = point[axis];
long above = coordinate - bounds[offset + axis * 2 + 1];
long below = bounds[offset + axis * 2] - coordinate;
long delta = above > 0 ? above : Math.max(below, 0L);
sum += delta * delta;
if (capped && sum >= limit) return sum;
}
return sum;
}
private int position(Object node) {
return node == null ? -1 : positions.get(node);
}
public Result search(long[] point, Object previous, Object penultimate) {
boolean capped = narrow;
for (long coordinate : point) capped &= coordinate >= -LIMIT && coordinate <= LIMIT;
return lookup(point, previous, penultimate, capped);
}
private Result lookup(long[] point, Object previous, Object penultimate, boolean capped) {
if (single())
return new Result(nodes[0], distance(0, point, 0, false), null, Long.MAX_VALUE);
int first = position(previous);
int second = position(penultimate);
long best = distance(first, point, 0, false);
long other = distance(second, point, 0, false);
if (best > other) {
int swap = first;
first = second;
second = swap;
long distance = best;
best = other;
other = distance;
}
int index = 1;
while (index < nodes.length) {
long candidate = distance(index, point, other, capped);
if (branches[index]) {
index = other > candidate ? index + 1 : next[index];
continue;
}
if (other > candidate) {
boolean different = !keys[index].equals(first < 0 ? nullKey : keys[first]);
if (best > candidate) {
if (different) {
other = best;
second = first;
}
best = candidate;
first = index;
} else if (different) {
other = candidate;
second = index;
}
}
index++;
}
return new Result(
first < 0 ? null : nodes[first], best, second < 0 ? null : nodes[second], other);
}
}

View file

@ -0,0 +1,152 @@
package dev.quorate.tune;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
public final class BiomePatch {
private static final String CLIMATE = "net/minecraft/world/level/biome/Climate$";
private static final String NODE = CLIMATE + "RTree$Node";
private static final String LEAF = CLIMATE + "RTree$Leaf";
private static final String BRANCH = CLIMATE + "RTree$SubTree";
private static final String RESULT = "com/terraformersmc/biolith/api/biome/BiolithFittestNodes";
private static final Type OBJECT = Type.getType(Object.class);
private BiomePatch() {}
public static int callers(ClassNode node) {
int count = 0;
for (var method : node.methods) {
if (!method.name.equals("getBiome")) continue;
for (var code : method.instructions.toArray()) {
if (!(code instanceof InvokeDynamicInsnNode factory)
|| factory.bsmArgs.length != 3
|| !(factory.bsmArgs[1] instanceof Handle handle)
|| !handle.getOwner().equals(NODE)
|| !handle.getName().equals("distance")
|| !handle.getDesc().equals("([J)J")) continue;
var next = code.getNext();
while (next != null && next.getOpcode() < 0) next = next.getNext();
if (!(next instanceof MethodInsnNode call)
|| !call.name.equals("biolith$searchTreeGet"))
throw new IllegalStateException("Biome caller shape");
method.instructions.set(
code,
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
CLIMATE + "TargetPoint",
"toParameterArray",
"()[J",
false));
var search =
new MethodInsnNode(
Opcodes.INVOKEINTERFACE,
"dev/quorate/tune/BiomeSearch",
"saltatory$search",
"([J)Ljava/lang/Object;",
true);
method.instructions.set(call, search);
method.instructions.insert(search, new TypeInsnNode(Opcodes.CHECKCAST, RESULT));
count++;
}
}
return count;
}
private static InstructionAdapter edit(MethodNode method) {
method.instructions.clear();
method.localVariables = null;
method.tryCatchBlocks.clear();
return new InstructionAdapter(method);
}
public static void tree(ClassNode node) {
String key =
node.methods.stream()
.filter(method -> method.name.endsWith("biolith$keyOf"))
.findFirst()
.orElseThrow()
.name;
for (var method : node.methods) {
String field =
switch (method.name) {
case "saltatory$root" -> "root";
case "saltatory$first" -> "previousUltimateNode";
case "saltatory$second" -> "previousPenultimateNode";
default -> null;
};
if (field != null) {
String descriptor =
node.fields.stream()
.filter(value -> value.name.equals(field))
.findFirst()
.orElseThrow()
.desc;
var emit = edit(method);
emit.load(0, OBJECT);
emit.getfield(node.name, field, descriptor);
emit.areturn(OBJECT);
emit.visitMaxs(1, 1);
} else if (method.name.equals("saltatory$children")) {
var emit = edit(method);
var leaf = new Label();
emit.load(1, OBJECT);
emit.instanceOf(Type.getObjectType(BRANCH));
emit.ifeq(leaf);
emit.load(1, OBJECT);
emit.checkcast(Type.getObjectType(BRANCH));
emit.getfield(BRANCH, "children", "[L" + NODE + ";");
emit.areturn(OBJECT);
emit.mark(leaf);
emit.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
emit.aconst(null);
emit.areturn(OBJECT);
emit.visitMaxs(1, 2);
} else if (method.name.equals("saltatory$bounds")) {
var emit = edit(method);
emit.load(1, OBJECT);
emit.checkcast(Type.getObjectType(NODE));
emit.getfield(NODE, "parameterSpace", "[L" + CLIMATE + "Parameter;");
emit.areturn(OBJECT);
emit.visitMaxs(1, 2);
} else if (method.name.equals("saltatory$key")) {
var emit = edit(method);
emit.load(0, OBJECT);
emit.load(1, OBJECT);
emit.checkcast(Type.getObjectType(LEAF));
emit.invokespecial(
node.name,
key,
"(L" + LEAF + ";)Lnet/minecraft/resources/ResourceKey;",
false);
emit.invokevirtual(
"net/minecraft/resources/ResourceKey",
"location",
"()Lnet/minecraft/resources/ResourceLocation;",
false);
emit.areturn(OBJECT);
emit.visitMaxs(2, 2);
} else if (method.name.equals("saltatory$result")) {
var emit = edit(method);
emit.anew(Type.getObjectType(RESULT));
emit.dup();
emit.load(1, OBJECT);
emit.checkcast(Type.getObjectType(LEAF));
emit.load(2, Type.LONG_TYPE);
emit.load(4, OBJECT);
emit.checkcast(Type.getObjectType(LEAF));
emit.load(5, Type.LONG_TYPE);
emit.invokespecial(RESULT, "<init>", "(L" + LEAF + ";JL" + LEAF + ";J)V", false);
emit.areturn(OBJECT);
emit.visitMaxs(8, 7);
}
}
}
}

View file

@ -0,0 +1,5 @@
package dev.quorate.tune;
public interface BiomeSearch {
Object saltatory$search(long[] point);
}

View file

@ -0,0 +1,46 @@
package dev.quorate.tune;
public final class CaveFrame {
private final double[][] a;
private final double[][] b;
private final double[][] c;
private final double[][] d;
private final double[][] values;
private final float[] start;
private final float[] end;
private final int last;
public CaveFrame(
double[][] a,
double[][] b,
double[][] c,
double[][] d,
float[] start,
float[] end,
int last) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.start = start;
this.end = end;
this.last = last;
values = new double[a.length][a[0].length];
}
public double[][] column(int x, int z) {
double sx = start[x];
double ex = end[x];
double sz = start[z];
double ez = end[z];
for (int y = 0; y < values.length; y++) {
double[] row = values[y];
for (int g = 0; g < row.length; g++) {
double left = x == 0 ? a[y][g] : x == last ? c[y][g] : a[y][g] * sx + c[y][g] * ex;
double right = x == 0 ? b[y][g] : x == last ? d[y][g] : b[y][g] * sx + d[y][g] * ex;
row[g] = z == 0 ? left : z == last ? right : left * sz + right * ez;
}
}
return values;
}
}

View file

@ -0,0 +1,7 @@
package dev.quorate.tune;
import net.minecraft.core.BlockPos;
public interface CaveNoise {
CaveFrame caveFrame(BlockPos min, BlockPos max, int low, int high);
}

View file

@ -0,0 +1,167 @@
package dev.quorate.tune;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
public final class CavePatch {
private static final String BC = "com/yungnickyoung/minecraft/bettercaves/";
private static final String RANGE = BC + "worldgen/carver/CarverNoiseRange";
private static final String CARVER = BC + "worldgen/carver/AbstractCarver";
private static final String NOISE = BC + "noise/NoiseGen";
private static final String POS = "Lnet/minecraft/core/BlockPos;";
private static final String CUBE = "(" + POS + POS + "II)[[[[D";
private static final String FRAME = Type.getInternalName(CaveFrame.class);
private static final String SOURCE = Type.getInternalName(CaveNoise.class);
private static final String FACTORY = "(" + POS + POS + "II)L" + FRAME + ";";
private static final String DEMAND =
"(Ljava/util/Map;Ljava/lang/Object;" + POS + POS + "III)[[D";
private static final String METHOD = "saltatory$noise";
private CavePatch() {}
public static void apply(ClassNode node) {
if (node.name.equals(NOISE)) {
node.interfaces.add(SOURCE);
node.methods.add(factory());
return;
}
if (node.name.endsWith("/CaveCarver")) {
CaveRows.apply(node);
return;
}
var body =
node.methods.stream()
.filter(method -> method.name.equals("carveChunk"))
.findFirst()
.orElseThrow();
int cubes = 0;
int demands = 0;
for (var instruction : body.instructions.toArray()) {
if (!(instruction instanceof MethodInsnNode call)) continue;
if (call.owner.equals(NOISE)
&& call.name.equals("interpolateNoiseCube")
&& call.desc.equals(CUBE)) {
var empty = new InsnList();
for (int argument = 0; argument < 5; argument++)
empty.add(new InsnNode(Opcodes.POP));
empty.add(new InsnNode(Opcodes.ACONST_NULL));
body.instructions.insertBefore(call, empty);
body.instructions.remove(call);
cubes++;
} else if (call.owner.equals("java/util/Map") && call.name.equals("get")) {
var extra = new InsnList();
extra.add(new VarInsnNode(Opcodes.ALOAD, local(body, "startPos")));
extra.add(new VarInsnNode(Opcodes.ALOAD, local(body, "endPos")));
extra.add(new VarInsnNode(Opcodes.ILOAD, local(body, "maxHeight")));
extra.add(new VarInsnNode(Opcodes.ILOAD, local(body, "offsetX")));
extra.add(new VarInsnNode(Opcodes.ILOAD, local(body, "offsetZ")));
body.instructions.insertBefore(call, extra);
AbstractInsnNode cursor = call;
for (int index = 0; index < 5; index++) {
AbstractInsnNode next = cursor.getNext();
while (next.getOpcode() < 0) next = next.getNext();
body.instructions.remove(next);
}
body.instructions.set(
call,
new MethodInsnNode(Opcodes.INVOKESTATIC, node.name, METHOD, DEMAND, false));
demands++;
}
}
if (cubes != 1 || demands != 1) throw new IllegalStateException("Cave layer shape changed");
body.maxStack += 5;
node.methods.add(demand());
}
private static int local(MethodNode method, String name) {
return method.localVariables.stream()
.filter(variable -> variable.name.equals(name))
.findFirst()
.orElseThrow()
.index;
}
private static MethodNode demand() {
var method =
new MethodNode(
Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, METHOD, DEMAND, null, null);
var emit = new InstructionAdapter(method);
var object = Type.getType(Object.class);
var found = new Label();
emit.load(0, object);
emit.load(1, object);
emit.invokeinterface("java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
emit.store(7, object);
emit.load(7, object);
emit.ifnonnull(found);
emit.load(1, object);
emit.checkcast(Type.getObjectType(RANGE));
emit.invokevirtual(RANGE, "getCarver", "()L" + CARVER + ";", false);
emit.store(8, object);
emit.load(8, object);
emit.invokevirtual(CARVER, "getNoiseGen", "()L" + NOISE + ";", false);
emit.checkcast(Type.getObjectType(SOURCE));
emit.load(2, object);
emit.load(3, object);
emit.load(8, object);
emit.invokevirtual(CARVER, "getBottomY", "()I", false);
emit.load(4, Type.INT_TYPE);
emit.invokeinterface(SOURCE, "caveFrame", FACTORY);
emit.store(7, object);
emit.load(0, object);
emit.load(1, object);
emit.load(7, object);
emit.invokeinterface(
"java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
emit.pop();
emit.mark(found);
emit.visitFrame(Opcodes.F_APPEND, 1, new Object[] {"java/lang/Object"}, 0, null);
emit.load(7, object);
emit.checkcast(Type.getObjectType(FRAME));
emit.load(5, Type.INT_TYPE);
emit.load(6, Type.INT_TYPE);
emit.invokevirtual(FRAME, "column", "(II)[[D", false);
emit.areturn(object);
emit.visitMaxs(5, 9);
return method;
}
private static MethodNode factory() {
var method = new MethodNode(Opcodes.ACC_PUBLIC, "caveFrame", FACTORY, null, null);
var emit = new InstructionAdapter(method);
var object = Type.getType(Object.class);
var pos = "net/minecraft/core/BlockPos";
emit.anew(Type.getObjectType(FRAME));
emit.dup();
for (int corner = 0; corner < 4; corner++) {
emit.load(0, object);
emit.load(corner / 2 + 1, object);
emit.invokevirtual(pos, "getX", "()I", false);
emit.load(corner % 2 + 1, object);
emit.invokevirtual(pos, "getZ", "()I", false);
emit.load(3, Type.INT_TYPE);
emit.load(4, Type.INT_TYPE);
emit.invokespecial(NOISE, "generateNoiseColumn", "(IIII)[[D", false);
}
emit.getstatic(BC + "BCConstants", "START_COEFFS", "[F");
emit.getstatic(BC + "BCConstants", "END_COEFFS", "[F");
emit.load(2, object);
emit.invokevirtual(pos, "getX", "()I", false);
emit.load(1, object);
emit.invokevirtual(pos, "getX", "()I", false);
emit.sub(Type.INT_TYPE);
emit.invokespecial(FRAME, "<init>", "([[D[[D[[D[[D[F[FI)V", false);
emit.areturn(object);
emit.visitMaxs(12, 5);
return method;
}
}

View file

@ -0,0 +1,106 @@
package dev.quorate.tune;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import java.util.List;
import java.util.Set;
public final class CaveRows {
private static final Set<String> METHODS =
Set.of("carveColumn", "generateThresholds", "preprocessCaveNoiseCol");
private CaveRows() {}
private static void frame(List<Object> values) {
if (values == null) return;
for (int i = 0; i < values.size(); i++)
if ("java/util/Map".equals(values.get(i)) || "java/util/HashMap".equals(values.get(i)))
values.set(i, "[F");
}
private static InsnList length() {
var code = new InsnList();
code.add(new VarInsnNode(Opcodes.ILOAD, 1));
code.add(new VarInsnNode(Opcodes.ILOAD, 2));
code.add(new InsnNode(Opcodes.ISUB));
code.add(new InsnNode(Opcodes.ICONST_1));
code.add(new InsnNode(Opcodes.IADD));
code.add(new InsnNode(Opcodes.ICONST_0));
code.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Math", "max", "(II)I", false));
code.add(new IntInsnNode(Opcodes.NEWARRAY, Opcodes.T_FLOAT));
return code;
}
private static InsnList offset(ClassNode owner, MethodNode method) {
var code = new InsnList();
switch (method.name) {
case "generateThresholds" -> code.add(new VarInsnNode(Opcodes.ILOAD, 2));
case "preprocessCaveNoiseCol" -> code.add(new VarInsnNode(Opcodes.ILOAD, 3));
default -> {
code.add(new VarInsnNode(Opcodes.ALOAD, 0));
code.add(new FieldInsnNode(Opcodes.GETFIELD, owner.name, "bottomY", "I"));
}
}
code.add(new InsnNode(Opcodes.ISUB));
return code;
}
public static void apply(ClassNode owner) {
int reads = 0;
int writes = 0;
for (var method : owner.methods) {
if (!METHODS.contains(method.name)) continue;
method.desc = method.desc.replace("Ljava/util/Map;", "[F");
method.signature = null;
method.localVariables = null;
method.maxStack += 2;
for (var instruction : method.instructions.toArray()) {
if (instruction instanceof FrameNode stack) {
frame(stack.local);
frame(stack.stack);
} else if (instruction instanceof TypeInsnNode type
&& type.desc.equals("java/util/HashMap")
&& type.getOpcode() == Opcodes.NEW) {
method.instructions.insertBefore(type, length());
method.instructions.remove(type.getNext());
method.instructions.remove(type);
} else if (instruction instanceof TypeInsnNode type
&& type.desc.equals("java/lang/Float")
&& type.getOpcode() == Opcodes.CHECKCAST) {
method.instructions.remove(type);
} else if (instruction instanceof MethodInsnNode call) {
if (call.owner.equals(owner.name) && METHODS.contains(call.name)) {
call.desc = call.desc.replace("Ljava/util/Map;", "[F");
} else if (call.owner.equals("java/lang/Integer")
&& call.name.equals("valueOf")) {
method.instructions.insertBefore(call, offset(owner, method));
method.instructions.remove(call);
} else if (call.owner.equals("java/util/Map") && call.name.equals("get")) {
method.instructions.set(call, new InsnNode(Opcodes.FALOAD));
reads++;
} else if (call.owner.equals("java/util/Map") && call.name.equals("put")) {
method.instructions.remove(call.getNext());
method.instructions.set(call, new InsnNode(Opcodes.FASTORE));
writes++;
} else if (call.owner.equals("java/lang/Float")
|| call.owner.equals("java/util/HashMap")
&& call.name.equals("<init>")) {
method.instructions.remove(call);
}
}
}
}
if (reads != 2 || writes != 1)
throw new IllegalStateException("Cave threshold shape changed");
}
}

View file

@ -0,0 +1,152 @@
package dev.quorate.tune;
import com.seibel.distanthorizons.core.api.internal.chunkUpdating.ChunkUpdateData;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
public final class ChunkQueue {
private static final class Entry {
final DhChunkPos pos;
final ChunkUpdateData value;
double distance;
int near;
int far;
Entry(DhChunkPos pos, ChunkUpdateData value, DhChunkPos center) {
this.pos = pos;
this.value = Objects.requireNonNull(value);
distance = pos.squaredDistance(center);
}
}
private static final class Heap {
private final boolean reverse;
private Entry[] entries = new Entry[500];
private int size;
Heap(boolean reverse) {
this.reverse = reverse;
}
int compare(Entry left, Entry right) {
return reverse
? Double.compare(right.distance, left.distance)
: Double.compare(left.distance, right.distance);
}
void put(int index, Entry entry) {
entries[index] = entry;
if (reverse) entry.far = index;
else entry.near = index;
}
void up(int index, Entry entry) {
while (index != 0) {
int parent = (index - 1) >>> 1;
Entry previous = entries[parent];
if (compare(entry, previous) >= 0) break;
put(index, previous);
index = parent;
}
put(index, entry);
}
void down(int index, Entry entry) {
int half = size >>> 1;
while (index < half) {
int child = (index << 1) + 1;
int sibling = child + 1;
if (sibling < size && compare(entries[child], entries[sibling]) > 0)
child = sibling;
if (compare(entry, entries[child]) <= 0) break;
put(index, entries[child]);
index = child;
}
put(index, entry);
}
void add(Entry entry) {
if (size == entries.length) entries = Arrays.copyOf(entries, size + (size >>> 1));
up(size++, entry);
}
Entry remove(int index) {
Entry removed = entries[index];
int last = --size;
Entry replacement = entries[last];
entries[last] = null;
if (index != last) {
down(index, replacement);
if (entries[index] == replacement) up(index, replacement);
}
return removed;
}
void clear() {
Arrays.fill(entries, 0, size, null);
size = 0;
}
}
private final HashMap<DhChunkPos, Entry> entries = new HashMap<>();
private final Heap near = new Heap(false);
private final Heap far = new Heap(true);
private DhChunkPos center = new DhChunkPos(0, 0);
public synchronized void addItem(DhChunkPos pos, ChunkUpdateData value) {
if (entries.containsKey(pos)) return;
var entry = new Entry(pos, value, center);
entries.put(pos, entry);
near.add(entry);
far.add(entry);
}
public synchronized boolean contains(DhChunkPos pos) {
return entries.containsKey(pos);
}
public synchronized int getQueuedCount() {
return entries.size();
}
public synchronized boolean isEmpty() {
return entries.isEmpty();
}
public synchronized void clear() {
entries.clear();
near.clear();
far.clear();
}
public synchronized void setCenter(DhChunkPos value) {
if (value.equals(center)) return;
center = value;
near.clear();
far.clear();
for (var entry : entries.values()) {
entry.distance = entry.pos.squaredDistance(center);
near.add(entry);
far.add(entry);
}
}
public synchronized ChunkUpdateData popClosest() {
if (near.size == 0) return null;
Entry entry = near.remove(0);
far.remove(entry.far);
entries.remove(entry.pos);
return entry.value;
}
public synchronized ChunkUpdateData popFurthest() {
if (far.size == 0) return null;
Entry entry = far.remove(0);
near.remove(entry.near);
entries.remove(entry.pos);
return entry.value;
}
}

View file

@ -0,0 +1,9 @@
package dev.quorate.tune;
import net.minecraft.world.level.levelgen.DensityFunction;
public interface DensityLimit {
int densityIndex();
double densityUpper(DensityFunction.FunctionContext point);
}

View file

@ -0,0 +1,27 @@
package dev.quorate.tune;
import net.minecraft.core.BlockPos;
public final class FeatureMath {
private FeatureMath() {}
public static int limit(int range, Object config) {
return ((GrowthView) config)
.placeBlock()
.getClass()
.getName()
.equals(
"net.mehvahdjukaar.supplementaries.common.block.blocks.BarnaclesBlock")
? Math.min(range, 1)
: range;
}
public static boolean allow(Object context, Object value, BlockPos pos) {
if (!(context instanceof StoneContext)) return true;
var shape = (ShapeView) value;
double x = (pos.getX() - shape.src().getX()) / shape.radius().x;
double y = (pos.getY() - shape.src().getY()) / shape.radius().y;
double z = (pos.getZ() - shape.src().getZ()) / shape.radius().z;
return !(x * x + y * y + z * z > 1.);
}
}

View file

@ -0,0 +1,7 @@
package dev.quorate.tune;
import net.minecraft.world.level.block.MultifaceBlock;
public interface GrowthView {
MultifaceBlock placeBlock();
}

View file

@ -0,0 +1,43 @@
package dev.quorate.tune;
import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import java.util.function.BiConsumer;
public final class LightScan {
// Keeping metadata above the int payload preserves negative static emission values.
private static final long DYNAMIC = 1L << 32;
private static final long UNKNOWN = 2L << 32;
private LightScan() {}
public static void find(ChunkAccess chunk, BiConsumer<BlockPos, BlockState> consumer) {
var emitters = new Reference2LongOpenHashMap<BlockState>();
emitters.defaultReturnValue(UNKNOWN);
chunk.findBlocks(
state -> sample(state, emitters) != 0,
(state, pos) -> {
long light = sample(state, emitters);
return (light == DYNAMIC ? state.getLightEmission(chunk, pos) : (int) light)
!= 0;
},
consumer);
}
private static long sample(BlockState state, Reference2LongOpenHashMap<BlockState> emitters) {
long light = emitters.getLong(state);
if (light != UNKNOWN) return light;
light =
state.hasDynamicLightEmission()
? DYNAMIC
: Integer.toUnsignedLong(
state.getLightEmission(EmptyBlockGetter.INSTANCE, BlockPos.ZERO));
emitters.put(state, light);
return light;
}
}

View file

@ -0,0 +1,5 @@
package dev.quorate.tune;
public interface LimitSource {
DensityLimit densityLimit();
}

View file

@ -0,0 +1,43 @@
package dev.quorate.tune;
public final class NoiseCache {
private static final int SIZE = 16384;
private static final ThreadLocal<NoiseCache> LOCAL = ThreadLocal.withInitial(NoiseCache::new);
private final Object[] owners = new Object[SIZE];
private final long[] xs = new long[SIZE];
private final long[] ys = new long[SIZE];
private final long[] zs = new long[SIZE];
private final double[] values = new double[SIZE];
public static NoiseCache local() {
return LOCAL.get();
}
// A miss returns the complemented slot, as in Arrays.binarySearch.
public int find(Object owner, long x, long y, long z) {
long key =
x
^ Long.rotateLeft(y, 21)
^ Long.rotateLeft(z, 42)
^ System.identityHashCode(owner);
key ^= key >>> 33;
key *= 0xff51afd7ed558ccdL;
key ^= key >>> 33;
int slot = (int) (key ^ (key >>> 32)) & (SIZE - 1);
return owners[slot] == owner && xs[slot] == x && ys[slot] == y && zs[slot] == z
? slot
: ~slot;
}
public double value(int slot) {
return values[slot];
}
public void put(int slot, Object owner, long x, long y, long z, double value) {
owners[slot] = owner;
xs[slot] = x;
ys[slot] = y;
zs[slot] = z;
values[slot] = value;
}
}

View file

@ -0,0 +1,33 @@
package dev.quorate.tune;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public final class NoiseCall {
private static final MethodHandle SAMPLE = binding();
private NoiseCall() {}
private static MethodHandle binding() {
try {
var type = Class.forName("com.ishland.c2me.opts.natives_math.common.Bindings");
return MethodHandles.publicLookup()
.findStatic(
type,
"c2me_natives_noise_perlin_double",
MethodType.methodType(
double.class,
long.class,
double.class,
double.class,
double.class));
} catch (ReflectiveOperationException failure) {
throw new ExceptionInInitializerError(failure);
}
}
public static double sample(long data, double x, double y, double z) throws Throwable {
return (double) SAMPLE.invokeExact(data, x, y, z);
}
}

View file

@ -0,0 +1,21 @@
package dev.quorate.tune;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import it.unimi.dsi.fastutil.shorts.ShortList;
import it.unimi.dsi.fastutil.shorts.ShortListIterator;
public final class PostOffsets {
private PostOffsets() {}
public static ShortListIterator snapshot(ShortList offsets) {
synchronized (offsets) {
return new ShortArrayList(offsets).iterator();
}
}
public static void remove(ShortList offsets, ShortListIterator iterator) {
synchronized (offsets) {
iterator.remove();
}
}
}

View file

@ -0,0 +1,10 @@
package dev.quorate.tune;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
public interface ShapeView {
BlockPos src();
Vec3 radius();
}

View file

@ -0,0 +1,3 @@
package dev.quorate.tune;
public interface StoneContext {}

View file

@ -0,0 +1,30 @@
package dev.quorate.tune;
import net.minecraft.util.Mth;
public final class SurfaceMath {
private SurfaceMath() {}
public static double value(double offset, double depth, double factor, int y) {
double gradient = Mth.clampedMap(y, -2048., 2048., 17., -15.);
double sum = offset + (gradient + depth);
double product = sum == 0. ? 0. : sum * factor;
double shaped = product > 0. ? product : product * .25;
return Math.max(-64., Math.min(64., -.7 + 4. * shaped));
}
public static int height(
double offset, double depth, double factor, int top, int bottom, int step) {
if (factor <= 0.)
return value(offset, depth, factor, top) > .390625 ? top : Integer.MAX_VALUE;
int count = (top - bottom) / step + 1;
int low = 0;
int high = count;
while (low < high) {
int mid = (low + high) >>> 1;
if (value(offset, depth, factor, top - mid * step) > .390625) high = mid;
else low = mid + 1;
}
return low == count ? Integer.MAX_VALUE : top - low * step;
}
}

Some files were not shown because too many files have changed in this diff Show more