Optimize world generation

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

View file

@ -1,13 +1,29 @@
# Third-party notices
| Upstream | License | Contribution to saltatory |
| ----------------------------------------------------------------------------------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Almanac 1.5.2](https://modrinth.com/mod/almanac), by frikinjay | LGPL-3.0-only | `AlmanacMixin` adapts `ItemNBTUtil` behavior from the released JAR's bytecode. Changes dated 2026-10-09 remove NBT copies and bypass normalization callbacks when custom data cannot change. |
| [Mekanism 10.7.19.85](https://github.com/mekanism/Mekanism/tree/a00109e4856fd38b9c5b3dd7f22ce4a59cd65a80) | MIT | `SlotMemo`, `FlowBatch`, `InvMixin`, and `MekMixin` adapt `TransporterManager` inventory prediction. Changes dated 2026-10-09 memoize slot validation within one prediction snapshot and eliminate a repeated stack compatibility check. |
| [Functional Storage 1.5.8](https://www.curseforge.com/minecraft/mc-mods/functional-storage/files/8459097), by Buuz135 and Rid | MIT | `MemberList`, `DrawersMixin`, and `SetupMixin` index handler membership and remove stream overhead. Modifications dated 2026-10-16. |
| [Refined Storage 2.0.9](https://modrinth.com/mod/refined-storage/version/lHHiI26k), by Refined Mods | MIT | `PlanRows` and `PlanMixin` detach shared ingredient rows before writing, preserving branch isolation. Modifications dated 2026-10-16. |
| [Ponder 1.0.82](https://github.com/Creators-of-Create/Ponder), by The Create Team | MIT | `ColorDraw` and `BatchDraw` adapt Catnip template emission. Modifications dated 2026-10-19 fuse buffer writes and omit unused shader attributes. |
| [Iris 1.8.12](https://github.com/IrisShaders/Iris), by coderbot and IMS212 | LGPL-3.0-only | `BatchDraw` and `QuadEnd` adapt extended quad completion. Modifications dated 2026-10-19 preserve deferred completion while skipping unconsumed attributes. |
| Upstream | License | Contribution to saltatory |
| ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Almanac 1.5.2](https://modrinth.com/mod/almanac), by frikinjay | LGPL-3.0-only | `AlmanacMixin` adapts `ItemNBTUtil` behavior from the released JAR's bytecode. Changes dated 2026-10-09 remove NBT copies and bypass normalization callbacks when custom data cannot change. |
| [Mekanism 10.7.19.85](https://github.com/mekanism/Mekanism/tree/a00109e4856fd38b9c5b3dd7f22ce4a59cd65a80) | MIT | `SlotMemo`, `FlowBatch`, `InvMixin`, and `MekMixin` adapt `TransporterManager` inventory prediction. Changes dated 2026-10-09 memoize slot validation within one prediction snapshot and eliminate a repeated stack compatibility check. |
| [Functional Storage 1.5.8](https://www.curseforge.com/minecraft/mc-mods/functional-storage/files/8459097), by Buuz135 and Rid | MIT | `MemberList`, `DrawersMixin`, and `SetupMixin` index handler membership and remove stream overhead. Modifications dated 2026-10-16. |
| [Refined Storage 2.0.9](https://modrinth.com/mod/refined-storage/version/lHHiI26k), by Refined Mods | MIT | `PlanRows` and `PlanMixin` detach shared ingredient rows before writing, preserving branch isolation. Modifications dated 2026-10-16. |
| [Ponder 1.0.82](https://github.com/Creators-of-Create/Ponder), by The Create Team | MIT | `ColorDraw` and `BatchDraw` adapt Catnip template emission. Modifications dated 2026-10-19 fuse buffer writes and omit unused shader attributes. |
| [Iris 1.8.12](https://github.com/IrisShaders/Iris), by coderbot and IMS212 | LGPL-3.0-only | `BatchDraw` and `QuadEnd` adapt extended quad completion. Modifications dated 2026-10-19 preserve deferred completion while skipping unconsumed attributes. |
| [Distant Horizons 3.3.3](https://gitlab.com/distant-horizons-team/distant-horizons) | LGPL-3.0 | Runtime hooks cache light emission and ID mappings and replace chunk-priority storage. Modifications dated 2026-11-09. |
| [YUNG's Better Caves 3.1.6](https://github.com/YUNG-GANG/YUNGs-Better-Caves), by YUNG | LGPL-3.0 | `CavePatch`, `CaveRows`, and `CaveFrame` adapt noise interpolation and threshold storage while retaining the upstream carving methods. Modifications dated 2026-11-09. |
| [Biolith 3.0.14](https://github.com/TerraformersMC/Biolith), by TerraformersMC and gniftygnome | LGPL-3.0 | `BiomeIndex` adapts nearest-biome search to flat storage; runtime hooks preserve upstream result types and cache history. Modifications dated 2026-11-09. |
| [C2ME](https://github.com/RelativityMC/C2ME-neoforge), by ishland | MIT | Runtime hooks use the density compiler's expression tree, memoize native noise, and index aquifer and structure inputs. Modifications dated 2026-11-09. |
| [Quark](https://github.com/VazkiiMods/Quark) and [Zeta](https://github.com/VazkiiMods/Zeta), by Violet Moon | CC-BY-NC-SA-3.0 | Runtime hooks skip stone-cluster probes outside the shape's ellipsoid. |
| [Supplementaries](https://github.com/MehVahdJukaar/Supplementaries), by the Supplementaries Team | Supplementaries Team License 1.5 | Runtime hooks omit repeated barnacle placement probes. |
## C2ME license
Copyright (c) 2021-2026 ishland
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## Mekanism license

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 {}

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