diff --git a/divinemc-server/minecraft-patches/features/0008-Misc-Optimizations.patch b/divinemc-server/minecraft-patches/features/0008-Misc-Optimizations.patch index 5c7a596..08722fb 100644 --- a/divinemc-server/minecraft-patches/features/0008-Misc-Optimizations.patch +++ b/divinemc-server/minecraft-patches/features/0008-Misc-Optimizations.patch @@ -327,106 +327,6 @@ index f28fbf81a417a678726d3f77b3999054676d522e..7ff32b1f93b31fafd13f4e0857d14d85 Arrays.fill(this.keys, null); Arrays.fill(this.byId, null); this.nextId = 0; -diff --git a/net/minecraft/util/CubicSpline.java b/net/minecraft/util/CubicSpline.java -index f36f8f2d49d4eba5c80eb243883749d6f831eb8a..5abd899c88683cb79bb8f02e43c4bfbe8563f496 100644 ---- a/net/minecraft/util/CubicSpline.java -+++ b/net/minecraft/util/CubicSpline.java -@@ -254,31 +254,47 @@ public interface CubicSpline> extends ToFloatFun - - @Override - public float apply(C object) { -- float f = this.coordinate.apply(object); -- int i = findIntervalStart(this.locations, f); -- int i1 = this.locations.length - 1; -- if (i < 0) { -- return linearExtend(f, this.locations, this.values.get(0).apply(object), this.derivatives, 0); -- } else if (i == i1) { -- return linearExtend(f, this.locations, this.values.get(i1).apply(object), this.derivatives, i1); -+ // DivineMC start - Some optimizations -+ float point = this.coordinate.apply(object); -+ int rangeForLocation = findIntervalStart(this.locations, point); -+ int last = this.locations.length - 1; -+ if (rangeForLocation < 0) { -+ return linearExtend(point, this.locations, this.values.get(0).apply(object), this.derivatives, 0); -+ } else if (rangeForLocation == last) { -+ return linearExtend(point, this.locations, this.values.get(last).apply(object), this.derivatives, last); - } else { -- float f1 = this.locations[i]; -- float f2 = this.locations[i + 1]; -- float f3 = (f - f1) / (f2 - f1); -- ToFloatFunction toFloatFunction = (ToFloatFunction)this.values.get(i); -- ToFloatFunction toFloatFunction1 = (ToFloatFunction)this.values.get(i + 1); -- float f4 = this.derivatives[i]; -- float f5 = this.derivatives[i + 1]; -- float f6 = toFloatFunction.apply(object); -- float f7 = toFloatFunction1.apply(object); -- float f8 = f4 * (f2 - f1) - (f7 - f6); -- float f9 = -f5 * (f2 - f1) + (f7 - f6); -- return Mth.lerp(f3, f6, f7) + f3 * (1.0F - f3) * Mth.lerp(f3, f8, f9); -+ float loc0 = this.locations[rangeForLocation]; -+ float loc1 = this.locations[rangeForLocation + 1]; -+ float locDist = loc1 - loc0; -+ float k = (point - loc0) / locDist; -+ float n = this.values.get(rangeForLocation).apply(object); -+ float o = this.values.get(rangeForLocation + 1).apply(object); -+ float onDist = o - n; -+ float p = this.derivatives[rangeForLocation] * locDist - onDist; -+ float q = -this.derivatives[rangeForLocation + 1] * locDist + onDist; -+ return Mth.lerp(k, n, o) + k * (1.0F - k) * Mth.lerp(k, p, q); - } -+ // DivineMC end - Some optimizations - } - - private static int findIntervalStart(float[] locations, float start) { -- return Mth.binarySearch(0, locations.length, i -> start < locations[i]) - 1; -+ // DivineMC start - Some optimizations -+ int min = 0; -+ int i = locations.length; -+ -+ while (i > 0) { -+ int j = i / 2; -+ int k = min + j; -+ if (start < locations[k]) { -+ i = j; -+ } else { -+ min = k + 1; -+ i -= j + 1; -+ } -+ } -+ -+ return min - 1; -+ // DivineMC end - Some optimizations - } - - @VisibleForTesting -@@ -313,5 +329,27 @@ public interface CubicSpline> extends ToFloatFun - this.derivatives - ); - } -+ -+ // DivineMC start - Some optimizations -+ @Override -+ public boolean equals(Object o) { -+ if (this == o) return true; -+ if (o == null || getClass() != o.getClass()) return false; -+ Multipoint that = (Multipoint) o; -+ return java.util.Objects.equals(coordinate, that.coordinate()) && java.util.Arrays.equals(locations, that.locations()) && java.util.Objects.equals(values, that.values()) && java.util.Arrays.equals(derivatives, that.derivatives()); -+ } -+ -+ @Override -+ public int hashCode() { -+ int result = 1; -+ -+ result = 31 * result + java.util.Objects.hashCode(coordinate); -+ result = 31 * result + java.util.Arrays.hashCode(locations); -+ result = 31 * result + java.util.Objects.hashCode(values); -+ result = 31 * result + java.util.Arrays.hashCode(derivatives); -+ -+ return result; -+ } -+ // DivineMC end - Some optimizations - } - } diff --git a/net/minecraft/util/Mth.java b/net/minecraft/util/Mth.java index ab3a221c115992d0f4ea921aa92cf0976b815ff4..076a931341da486162f289a5f19d3d6736df7768 100644 --- a/net/minecraft/util/Mth.java @@ -665,7 +565,7 @@ index 6b50a3fc0476f4b9941f1e66fb3f2a79042e33aa..1fdbd88841993e377ea2e14b40d059dd int floor1 = Mth.floor(y); int floor2 = Mth.floor(z); diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java -index 5bcbebf35eb726a43b957aca5b8b7a1dca7648cd..d618752727e2f2f5c0c1afa97f455e349cb7e76c 100644 +index a58133b53a19e90d4386f57891ee41a5c03228c2..a65c86b411c15bbdfd431dac00e510d2262e65e1 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java @@ -698,7 +698,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab @@ -698,7 +598,7 @@ index 5bcbebf35eb726a43b957aca5b8b7a1dca7648cd..d618752727e2f2f5c0c1afa97f455e34 protected final void serverAiStep() { this.noActionTime++; diff --git a/net/minecraft/world/level/GameRules.java b/net/minecraft/world/level/GameRules.java -index 02bc5d83b92a594ec519f0a02b0517fdb4b9e954..86c1f5effde3173c8bc458af21b454c81738935e 100644 +index 92fe6acbd530e985da23f50e615817309915122c..c9a2c4f7051639478bd9788911d3c6bead8f5152 100644 --- a/net/minecraft/world/level/GameRules.java +++ b/net/minecraft/world/level/GameRules.java @@ -277,7 +277,7 @@ public class GameRules { diff --git a/patches/removed/1.21.4/server/0031-Density-Function-Compiler.patch b/divinemc-server/minecraft-patches/features/0050-Density-Function-Compiler.patch similarity index 99% rename from patches/removed/1.21.4/server/0031-Density-Function-Compiler.patch rename to divinemc-server/minecraft-patches/features/0050-Density-Function-Compiler.patch index 82848f2..c9c785a 100644 --- a/patches/removed/1.21.4/server/0031-Density-Function-Compiler.patch +++ b/divinemc-server/minecraft-patches/features/0050-Density-Function-Compiler.patch @@ -116,7 +116,7 @@ index f36f8f2d49d4eba5c80eb243883749d6f831eb8a..b43b7e242ea0a4f87704853c03201144 } } diff --git a/net/minecraft/world/level/levelgen/DensityFunctions.java b/net/minecraft/world/level/levelgen/DensityFunctions.java -index 7178013421233d7dab36eb07a768907ce40e8745..f56321eefa5fcdfdb30883beaf97c87ac6fa0183 100644 +index 4c53031cf8f6198825b190955d96f20bbcccd77e..8827b7525de30ce15fccb9ac9f073110ba6c7b50 100644 --- a/net/minecraft/world/level/levelgen/DensityFunctions.java +++ b/net/minecraft/world/level/levelgen/DensityFunctions.java @@ -275,38 +275,66 @@ public final class DensityFunctions { @@ -215,7 +215,7 @@ index 7178013421233d7dab36eb07a768907ce40e8745..f56321eefa5fcdfdb30883beaf97c87a } @Override -@@ -704,7 +732,105 @@ public final class DensityFunctions { +@@ -683,7 +711,105 @@ public final class DensityFunctions { } } @@ -322,7 +322,7 @@ index 7178013421233d7dab36eb07a768907ce40e8745..f56321eefa5fcdfdb30883beaf97c87a @Override public double compute(DensityFunction.FunctionContext context) { return this.wrapped.compute(context); -@@ -725,7 +851,19 @@ public final class DensityFunctions { +@@ -704,7 +830,19 @@ public final class DensityFunctions { return this.wrapped.maxValue(); } @@ -337,9 +337,9 @@ index 7178013421233d7dab36eb07a768907ce40e8745..f56321eefa5fcdfdb30883beaf97c87a + public DensityFunction wrapped() { + return wrapped; + } -+ -+ public static enum Type implements StringRepresentable { // - public + // DivineMC end - Density Function Compiler ++ ++ public static enum Type implements StringRepresentable { // DivineMC - Density Function Compiler - make this public Interpolated("interpolated"), FlatCache("flat_cache"), Cache2D("cache_2d"), diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/DivineConfig.java b/divinemc-server/src/main/java/org/bxteam/divinemc/DivineConfig.java index b8aed6e..288a263 100644 --- a/divinemc-server/src/main/java/org/bxteam/divinemc/DivineConfig.java +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/DivineConfig.java @@ -191,6 +191,7 @@ public class DivineConfig { public static boolean enableSecureSeed = false; public static boolean smoothBedrockLayer = false; public static boolean slopesVisualFix = false; + public static boolean enableDensityFunctionCompiler = false; public static boolean enableStructureLayoutOptimizer = true; public static boolean deduplicateShuffledTemplatePoolElementList = false; private static void chunkSettings() { @@ -238,6 +239,18 @@ public class DivineConfig { slopesVisualFix = getBoolean("settings.chunks.slopes-visual-fix", slopesVisualFix, "Fixes MC-258859, fixing slopes visual bug in biomes like Snowy Slopes, Frozen Peaks, Jagged Peaks, and including Terralith."); + enableDensityFunctionCompiler = getBoolean("settings.chunk-generation.experimental.enable-density-function-compiler", enableDensityFunctionCompiler, + "Whether to use density function compiler to accelerate world generation", + "", + "Density function: https://minecraft.wiki/w/Density_function", + "", + "This functionality compiles density functions from world generation", + "datapacks (including vanilla generation) to JVM bytecode to increase", + "performance by allowing JVM JIT to better optimize the code.", + "All functions provided by vanilla are implemented.", + "", + "Please test if this optimization actually benefits your server, as", + "it can sometimes slow down chunk performance than speed it up."); enableStructureLayoutOptimizer = getBoolean("settings.chunks.experimental.enable-structure-layout-optimizer", enableStructureLayoutOptimizer, "Enables a port of the mod StructureLayoutOptimizer, which optimizes general Jigsaw structure generation"); deduplicateShuffledTemplatePoolElementList = getBoolean("settings.chunks.experimental.deduplicate-shuffled-template-pool-element-list", deduplicateShuffledTemplatePoolElementList, diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/IDensityFunctionsCaveScaler.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/IDensityFunctionsCaveScaler.java new file mode 100644 index 0000000..1fa43ca --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/IDensityFunctionsCaveScaler.java @@ -0,0 +1,13 @@ +package org.bxteam.divinemc.dfc.common; + +import net.minecraft.world.level.levelgen.NoiseRouterData; + +public interface IDensityFunctionsCaveScaler { + static double invokeScaleCaves(double value) { + return NoiseRouterData.QuantizedSpaghettiRarity.getSphaghettiRarity2D(value); + } + + static double invokeScaleTunnels(double value) { + return NoiseRouterData.QuantizedSpaghettiRarity.getSpaghettiRarity3D(value); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/AstNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/AstNode.java new file mode 100644 index 0000000..c61fcb7 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/AstNode.java @@ -0,0 +1,22 @@ +package org.bxteam.divinemc.dfc.common.ast; + +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.commons.InstructionAdapter; + +public interface AstNode { + double evalSingle(int var1, int var2, int var3, EvalType var4); + + void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5); + + AstNode[] getChildren(); + + AstNode transform(AstTransformer var1); + + void doBytecodeGenSingle(BytecodeGen.Context var1, InstructionAdapter var2, BytecodeGen.Context.LocalVarConsumer var3); + + void doBytecodeGenMulti(BytecodeGen.Context var1, InstructionAdapter var2, BytecodeGen.Context.LocalVarConsumer var3); + + boolean relaxedEquals(AstNode var1); + + int relaxedHashCode(); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/AstTransformer.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/AstTransformer.java new file mode 100644 index 0000000..81017d3 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/AstTransformer.java @@ -0,0 +1,5 @@ +package org.bxteam.divinemc.dfc.common.ast; + +public interface AstTransformer { + AstNode transform(AstNode var1); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/EvalType.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/EvalType.java new file mode 100644 index 0000000..3e19f35 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/EvalType.java @@ -0,0 +1,25 @@ +package org.bxteam.divinemc.dfc.common.ast; + +import org.bxteam.divinemc.dfc.common.vif.EachApplierVanillaInterface; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.NoiseChunk; + +public enum EvalType { + NORMAL, + INTERPOLATION; + + private EvalType() { + } + + public static EvalType from(DensityFunction.FunctionContext pos) { + return pos instanceof NoiseChunk ? INTERPOLATION : NORMAL; + } + + public static EvalType from(DensityFunction.ContextProvider applier) { + if (applier instanceof EachApplierVanillaInterface vif) { + return vif.getType(); + } else { + return applier instanceof NoiseChunk ? INTERPOLATION : NORMAL; + } + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/McToAst.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/McToAst.java new file mode 100644 index 0000000..532e6fb --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/McToAst.java @@ -0,0 +1,106 @@ +package org.bxteam.divinemc.dfc.common.ast; + +import org.bxteam.divinemc.dfc.common.ast.binary.AddNode; +import org.bxteam.divinemc.dfc.common.ast.binary.MaxNode; +import org.bxteam.divinemc.dfc.common.ast.binary.MaxShortNode; +import org.bxteam.divinemc.dfc.common.ast.binary.MinNode; +import org.bxteam.divinemc.dfc.common.ast.binary.MinShortNode; +import org.bxteam.divinemc.dfc.common.ast.binary.MulNode; +import org.bxteam.divinemc.dfc.common.ast.misc.CacheLikeNode; +import org.bxteam.divinemc.dfc.common.ast.misc.ConstantNode; +import org.bxteam.divinemc.dfc.common.ast.misc.DelegateNode; +import org.bxteam.divinemc.dfc.common.ast.misc.RangeChoiceNode; +import org.bxteam.divinemc.dfc.common.ast.misc.YClampedGradientNode; +import org.bxteam.divinemc.dfc.common.ast.noise.DFTNoiseNode; +import org.bxteam.divinemc.dfc.common.ast.noise.DFTShiftANode; +import org.bxteam.divinemc.dfc.common.ast.noise.DFTShiftBNode; +import org.bxteam.divinemc.dfc.common.ast.noise.DFTShiftNode; +import org.bxteam.divinemc.dfc.common.ast.noise.DFTWeirdScaledSamplerNode; +import org.bxteam.divinemc.dfc.common.ast.noise.ShiftedNoiseNode; +import org.bxteam.divinemc.dfc.common.ast.spline.SplineAstNode; +import org.bxteam.divinemc.dfc.common.ast.unary.AbsNode; +import org.bxteam.divinemc.dfc.common.ast.unary.CubeNode; +import org.bxteam.divinemc.dfc.common.ast.unary.NegMulNode; +import org.bxteam.divinemc.dfc.common.ast.unary.SquareNode; +import org.bxteam.divinemc.dfc.common.ast.unary.SqueezeNode; +import org.bxteam.divinemc.dfc.common.ducks.IEqualityOverriding; +import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike; +import org.bxteam.divinemc.dfc.common.vif.AstVanillaInterface; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.DensityFunctions; +import net.minecraft.world.level.levelgen.NoiseChunk; +import org.jetbrains.annotations.NotNull; + +public class McToAst { + public McToAst() { + } + + public static AstNode toAst(DensityFunction df) { + Objects.requireNonNull(df); + return switch (df) { + case AstVanillaInterface f -> f.getAstNode(); + + case NoiseChunk.BlendAlpha f -> new ConstantNode(1.0); + case NoiseChunk.BlendOffset f -> new ConstantNode(0.0); + case DensityFunctions.BlendAlpha f -> new ConstantNode(1.0); + case DensityFunctions.BlendOffset f -> new ConstantNode(0.0); + case DensityFunctions.TwoArgumentSimpleFunction f -> switch (f.type()) { + case ADD -> new AddNode(toAst(f.argument1()), toAst(f.argument2())); + case MUL -> new MulNode(toAst(f.argument1()), toAst(f.argument2())); + case MIN -> { + double rightMin = f.argument2().minValue(); + if (f.argument1().minValue() < rightMin) { + yield new MinShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMin); + } else { + yield new MinNode(toAst(f.argument1()), toAst(f.argument2())); + } + } + case MAX -> { + double rightMax = f.argument2().maxValue(); + if (f.argument1().maxValue() > rightMax) { + yield new MaxShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMax); + } else { + yield new MaxNode(toAst(f.argument1()), toAst(f.argument2())); + } + } + }; + case DensityFunctions.BlendDensity f -> toAst(f.input()); + case DensityFunctions.Clamp f -> new MaxNode(new ConstantNode(f.minValue()), new MinNode(new ConstantNode(f.maxValue()), toAst(f.input()))); + case DensityFunctions.Constant f -> new ConstantNode(f.value()); + case DensityFunctions.HolderHolder f -> toAst(f.function().value()); + case DensityFunctions.Mapped f -> switch (f.type()) { + case ABS -> new AbsNode(toAst(f.input())); + case SQUARE -> new SquareNode(toAst(f.input())); + case CUBE -> new CubeNode(toAst(f.input())); + case HALF_NEGATIVE -> new NegMulNode(toAst(f.input()), 0.5); + case QUARTER_NEGATIVE -> new NegMulNode(toAst(f.input()), 0.25); + case SQUEEZE -> new SqueezeNode(toAst(f.input())); + }; + case DensityFunctions.RangeChoice f -> new RangeChoiceNode(toAst(f.input()), f.minInclusive(), f.maxExclusive(), toAst(f.whenInRange()), toAst(f.whenOutOfRange())); + case DensityFunctions.Marker f -> { + DensityFunctions.Marker wrapping = new DensityFunctions.Marker(f.type(), new AstVanillaInterface(toAst(f.wrapped()), null)); + ((IEqualityOverriding) (Object) wrapping).c2me$overrideEquality(wrapping); + yield new DelegateNode(wrapping); + } + case IFastCacheLike f -> new CacheLikeNode(f, toAst(f.c2me$getDelegate())); + case DensityFunctions.ShiftedNoise f -> new ShiftedNoiseNode(toAst(f.shiftX()), toAst(f.shiftY()), toAst(f.shiftZ()), f.xzScale(), f.yScale(), f.noise()); + case DensityFunctions.Noise f -> new DFTNoiseNode(f.noise(), f.xzScale(), f.yScale()); + case DensityFunctions.Shift f -> new DFTShiftNode(f.offsetNoise()); + case DensityFunctions.ShiftA f -> new DFTShiftANode(f.offsetNoise()); + case DensityFunctions.ShiftB f -> new DFTShiftBNode(f.offsetNoise()); + case DensityFunctions.YClampedGradient f -> new YClampedGradientNode(f.fromY(), f.toY(), f.fromValue(), f.toValue()); + case DensityFunctions.WeirdScaledSampler f -> new DFTWeirdScaledSamplerNode(toAst(f.input()), f.noise(), f.rarityValueMapper()); + case DensityFunctions.Spline f -> new SplineAstNode(f.spline()); + + default -> { +// delegateStatistics.computeIfAbsent(df.getClass(), unused -> new LongAdder()).increment();; + yield new DelegateNode(df); + } + }; + } + + public static @NotNull DensityFunction wrapVanilla(DensityFunction densityFunction) { + return new AstVanillaInterface(toAst(densityFunction), densityFunction); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/AbstractBinaryNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/AbstractBinaryNode.java new file mode 100644 index 0000000..7c01c36 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/AbstractBinaryNode.java @@ -0,0 +1,106 @@ +package org.bxteam.divinemc.dfc.common.ast.binary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context; +import org.bxteam.divinemc.dfc.common.util.ArrayCache; +import java.util.Objects; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public abstract class AbstractBinaryNode implements AstNode { + protected final AstNode left; + protected final AstNode right; + + public AbstractBinaryNode(AstNode left, AstNode right) { + this.left = (AstNode)Objects.requireNonNull(left); + this.right = (AstNode)Objects.requireNonNull(right); + } + + public AstNode[] getChildren() { + return new AstNode[]{this.left, this.right}; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + AbstractBinaryNode that = (AbstractBinaryNode)o; + return Objects.equals(this.left, that.left) && Objects.equals(this.right, that.right); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.left.hashCode(); + result = 31 * result + this.right.hashCode(); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + AbstractBinaryNode that = (AbstractBinaryNode)o; + return this.left.relaxedEquals(that.left) && this.right.relaxedEquals(that.right); + } else { + return false; + } + } + + public int relaxedHashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.left.relaxedHashCode(); + result = 31 * result + this.right.relaxedHashCode(); + return result; + } + + protected abstract AstNode newInstance(AstNode var1, AstNode var2); + + public AstNode transform(AstTransformer transformer) { + AstNode left = this.left.transform(transformer); + AstNode right = this.right.transform(transformer); + return left == this.left && right == this.right ? transformer.transform(this) : transformer.transform(this.newInstance(left, right)); + } + + public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newSingleMethod(this.left); + String rightMethod = context.newSingleMethod(this.right); + context.callDelegateSingle(m, leftMethod); + context.callDelegateSingle(m, rightMethod); + } + + public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newMultiMethod(this.left); + String rightMethod = context.newMultiMethod(this.right); + int res1 = localVarConsumer.createLocalVariable("res1", Type.getDescriptor(double[].class)); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.arraylength(); + m.iconst(0); + m.invokevirtual(Type.getInternalName(ArrayCache.class), "getDoubleArray", Type.getMethodDescriptor(Type.getType(double[].class), new Type[]{Type.INT_TYPE, Type.BOOLEAN_TYPE}), false); + m.store(res1, InstructionAdapter.OBJECT_TYPE); + context.callDelegateMulti(m, leftMethod); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(res1, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, rightMethod, Context.MULTI_DESC, false); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + this.bytecodeGenMultiBody(m, idx, res1); + }); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.load(res1, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(Type.getInternalName(ArrayCache.class), "recycle", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class)}), false); + m.areturn(Type.VOID_TYPE); + } + + protected abstract void bytecodeGenMultiBody(InstructionAdapter var1, int var2, int var3); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/AddNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/AddNode.java new file mode 100644 index 0000000..53b50d6 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/AddNode.java @@ -0,0 +1,50 @@ +package org.bxteam.divinemc.dfc.common.ast.binary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class AddNode extends AbstractBinaryNode { + public AddNode(AstNode left, AstNode right) { + super(left, right); + } + + protected AstNode newInstance(AstNode left, AstNode right) { + return new AddNode(left, right); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return this.left.evalSingle(x, y, z, type) + this.right.evalSingle(x, y, z, type); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + double[] res1 = new double[res.length]; + this.left.evalMulti(res, x, y, z, type); + this.right.evalMulti(res1, x, y, z, type); + + for(int i = 0; i < res1.length; ++i) { + res[i] += res1[i]; + } + + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenSingle(context, m, localVarConsumer); + m.add(Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.dup2(); + m.aload(Type.DOUBLE_TYPE); + m.load(res1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.add(Type.DOUBLE_TYPE); + m.astore(Type.DOUBLE_TYPE); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MaxNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MaxNode.java new file mode 100644 index 0000000..bed1a4c --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MaxNode.java @@ -0,0 +1,50 @@ +package org.bxteam.divinemc.dfc.common.ast.binary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class MaxNode extends AbstractBinaryNode { + public MaxNode(AstNode left, AstNode right) { + super(left, right); + } + + protected AstNode newInstance(AstNode left, AstNode right) { + return new MaxNode(left, right); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return Math.max(this.left.evalSingle(x, y, z, type), this.right.evalSingle(x, y, z, type)); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + double[] res1 = new double[res.length]; + this.left.evalMulti(res, x, y, z, type); + this.right.evalMulti(res1, x, y, z, type); + + for(int i = 0; i < res1.length; ++i) { + res[i] = Math.max(res[i], res1[i]); + } + + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenSingle(context, m, localVarConsumer); + m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.areturn(Type.DOUBLE_TYPE); + } + + protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.dup2(); + m.aload(Type.DOUBLE_TYPE); + m.load(res1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.astore(Type.DOUBLE_TYPE); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MaxShortNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MaxShortNode.java new file mode 100644 index 0000000..8cab854 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MaxShortNode.java @@ -0,0 +1,92 @@ +package org.bxteam.divinemc.dfc.common.ast.binary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class MaxShortNode extends AbstractBinaryNode { + private final double rightMax; + + public MaxShortNode(AstNode left, AstNode right, double rightMax) { + super(left, right); + this.rightMax = rightMax; + } + + protected AstNode newInstance(AstNode left, AstNode right) { + return new MaxShortNode(left, right, this.rightMax); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double evaled = this.left.evalSingle(x, y, z, type); + return evaled >= this.rightMax ? evaled : Math.max(evaled, this.right.evalSingle(x, y, z, type)); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.left.evalMulti(res, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + res[i] = res[i] >= this.rightMax ? res[i] : Math.max(res[i], this.right.evalSingle(x[i], y[i], z[i], type)); + } + + } + + public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newSingleMethod(this.left); + String rightMethod = context.newSingleMethod(this.right); + Label minLabel = new Label(); + context.callDelegateSingle(m, leftMethod); + m.dup2(); + m.dconst(this.rightMax); + m.cmpl(Type.DOUBLE_TYPE); + m.iflt(minLabel); + m.areturn(Type.DOUBLE_TYPE); + m.visitLabel(minLabel); + context.callDelegateSingle(m, rightMethod); + m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newMultiMethod(this.left); + String rightMethodSingle = context.newSingleMethod(this.right); + context.callDelegateMulti(m, leftMethod); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + Label minLabel = new Label(); + Label end = new Label(); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.dup2(); + m.dconst(this.rightMax); + m.cmpl(Type.DOUBLE_TYPE); + m.iflt(minLabel); + m.goTo(end); + m.visitLabel(minLabel); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, rightMethodSingle, Context.SINGLE_DESC, false); + m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.visitLabel(end); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) { + throw new UnsupportedOperationException(); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MinNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MinNode.java new file mode 100644 index 0000000..909180b --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MinNode.java @@ -0,0 +1,50 @@ +package org.bxteam.divinemc.dfc.common.ast.binary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class MinNode extends AbstractBinaryNode { + public MinNode(AstNode left, AstNode right) { + super(left, right); + } + + protected AstNode newInstance(AstNode left, AstNode right) { + return new MinNode(left, right); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return Math.min(this.left.evalSingle(x, y, z, type), this.right.evalSingle(x, y, z, type)); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + double[] res1 = new double[res.length]; + this.left.evalMulti(res, x, y, z, type); + this.right.evalMulti(res1, x, y, z, type); + + for(int i = 0; i < res1.length; ++i) { + res[i] = Math.min(res[i], res1[i]); + } + + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenSingle(context, m, localVarConsumer); + m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.areturn(Type.DOUBLE_TYPE); + } + + protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.dup2(); + m.aload(Type.DOUBLE_TYPE); + m.load(res1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.astore(Type.DOUBLE_TYPE); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MinShortNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MinShortNode.java new file mode 100644 index 0000000..615ca64 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MinShortNode.java @@ -0,0 +1,92 @@ +package org.bxteam.divinemc.dfc.common.ast.binary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class MinShortNode extends AbstractBinaryNode { + private final double rightMin; + + public MinShortNode(AstNode left, AstNode right, double rightMin) { + super(left, right); + this.rightMin = rightMin; + } + + protected AstNode newInstance(AstNode left, AstNode right) { + return new MinShortNode(left, right, this.rightMin); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double evaled = this.left.evalSingle(x, y, z, type); + return evaled <= this.rightMin ? evaled : Math.min(evaled, this.right.evalSingle(x, y, z, type)); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.left.evalMulti(res, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + res[i] = res[i] <= this.rightMin ? res[i] : Math.min(res[i], this.right.evalSingle(x[i], y[i], z[i], type)); + } + + } + + public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newSingleMethod(this.left); + String rightMethod = context.newSingleMethod(this.right); + Label minLabel = new Label(); + context.callDelegateSingle(m, leftMethod); + m.dup2(); + m.dconst(this.rightMin); + m.cmpg(Type.DOUBLE_TYPE); + m.ifgt(minLabel); + m.areturn(Type.DOUBLE_TYPE); + m.visitLabel(minLabel); + context.callDelegateSingle(m, rightMethod); + m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newMultiMethod(this.left); + String rightMethodSingle = context.newSingleMethod(this.right); + context.callDelegateMulti(m, leftMethod); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + Label minLabel = new Label(); + Label end = new Label(); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.dup2(); + m.dconst(this.rightMin); + m.cmpg(Type.DOUBLE_TYPE); + m.ifgt(minLabel); + m.goTo(end); + m.visitLabel(minLabel); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, rightMethodSingle, Context.SINGLE_DESC, false); + m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.visitLabel(end); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) { + throw new UnsupportedOperationException(); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MulNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MulNode.java new file mode 100644 index 0000000..87cc44c --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/binary/MulNode.java @@ -0,0 +1,92 @@ +package org.bxteam.divinemc.dfc.common.ast.binary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class MulNode extends AbstractBinaryNode { + public MulNode(AstNode left, AstNode right) { + super(left, right); + } + + protected AstNode newInstance(AstNode left, AstNode right) { + return new MulNode(left, right); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double evaled = this.left.evalSingle(x, y, z, type); + return evaled == 0.0 ? 0.0 : evaled * this.right.evalSingle(x, y, z, type); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.left.evalMulti(res, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + res[i] = res[i] == 0.0 ? 0.0 : res[i] * this.right.evalSingle(x[i], y[i], z[i], type); + } + + } + + public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newSingleMethod(this.left); + String rightMethod = context.newSingleMethod(this.right); + Label notZero = new Label(); + context.callDelegateSingle(m, leftMethod); + m.dup2(); + m.dconst(0.0); + m.cmpl(Type.DOUBLE_TYPE); + m.ifne(notZero); + m.dconst(0.0); + m.areturn(Type.DOUBLE_TYPE); + m.visitLabel(notZero); + context.callDelegateSingle(m, rightMethod); + m.mul(Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newMultiMethod(this.left); + String rightMethodSingle = context.newSingleMethod(this.right); + context.callDelegateMulti(m, leftMethod); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + Label minLabel = new Label(); + Label end = new Label(); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.dup2(); + m.dconst(0.0); + m.cmpl(Type.DOUBLE_TYPE); + m.ifne(minLabel); + m.pop2(); + m.dconst(0.0); + m.goTo(end); + m.visitLabel(minLabel); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, rightMethodSingle, Context.SINGLE_DESC, false); + m.mul(Type.DOUBLE_TYPE); + m.visitLabel(end); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) { + throw new UnsupportedOperationException(); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/dfvisitor/StripBlending.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/dfvisitor/StripBlending.java new file mode 100644 index 0000000..ad9fa65 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/dfvisitor/StripBlending.java @@ -0,0 +1,23 @@ +package org.bxteam.divinemc.dfc.common.ast.dfvisitor; + +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.DensityFunctions; +import net.minecraft.world.level.levelgen.NoiseChunk; +import org.jetbrains.annotations.NotNull; + +public class StripBlending implements DensityFunction.Visitor { + public static final StripBlending INSTANCE = new StripBlending(); + + private StripBlending() { + } + + public @NotNull DensityFunction apply(@NotNull DensityFunction densityFunction) { + return switch (densityFunction) { + case NoiseChunk.BlendAlpha ignored -> DensityFunctions.constant(1.0); + case NoiseChunk.BlendOffset ignored -> DensityFunctions.constant(0.0); + case DensityFunctions.BlendAlpha ignored -> DensityFunctions.constant(1.0); + case DensityFunctions.BlendOffset ignored -> DensityFunctions.constant(0.0); + default -> densityFunction; + }; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/CacheLikeNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/CacheLikeNode.java new file mode 100644 index 0000000..d9a2689 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/CacheLikeNode.java @@ -0,0 +1,256 @@ +package org.bxteam.divinemc.dfc.common.ast.misc; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context; +import org.bxteam.divinemc.dfc.common.gen.IMultiMethod; +import org.bxteam.divinemc.dfc.common.gen.ISingleMethod; +import org.bxteam.divinemc.dfc.common.gen.SubCompiledDensityFunction; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.DensityFunctions; +import org.jetbrains.annotations.NotNull; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class CacheLikeNode implements AstNode { + private final IFastCacheLike cacheLike; + private final AstNode delegate; + + public CacheLikeNode(IFastCacheLike cacheLike, AstNode delegate) { + this.cacheLike = cacheLike; + this.delegate = (AstNode)Objects.requireNonNull(delegate); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + if (this.cacheLike == null) { + return this.delegate.evalSingle(x, y, z, type); + } else { + double cached = this.cacheLike.c2me$getCached(x, y, z, type); + if (Double.doubleToRawLongBits(cached) != 9222769054270909007L) { + return cached; + } else { + double eval = this.delegate.evalSingle(x, y, z, type); + this.cacheLike.c2me$cache(x, y, z, type, eval); + return eval; + } + } + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + if (this.cacheLike == null) { + this.delegate.evalMulti(res, x, y, z, type); + } else { + boolean cached = this.cacheLike.c2me$getCached(res, x, y, z, type); + if (!cached) { + this.delegate.evalMulti(res, x, y, z, type); + this.cacheLike.c2me$cache(res, x, y, z, type); + } + + } + } + + public AstNode[] getChildren() { + return new AstNode[]{this.delegate}; + } + + public AstNode transform(AstTransformer transformer) { + AstNode delegate = this.delegate.transform(transformer); + return this.delegate == delegate ? transformer.transform(this) : transformer.transform(new CacheLikeNode(this.cacheLike, delegate)); + } + + public void doBytecodeGenSingle(@NotNull Context context, @NotNull InstructionAdapter m, Context.@NotNull LocalVarConsumer localVarConsumer) { + String delegateMethod = context.newSingleMethod(this.delegate); + String cacheLikeField = context.newField(IFastCacheLike.class, this.cacheLike); + this.genPostprocessingMethod(context, cacheLikeField); + int eval = localVarConsumer.createLocalVariable("eval", Type.DOUBLE_TYPE.getDescriptor()); + Label cacheExists = new Label(); + Label cacheMiss = new Label(); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class)); + m.ifnonnull(cacheExists); + context.callDelegateSingle(m, delegateMethod); + m.areturn(Type.DOUBLE_TYPE); + m.visitLabel(cacheExists); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class)); + m.load(1, Type.INT_TYPE); + m.load(2, Type.INT_TYPE); + m.load(3, Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$getCached", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class)})); + m.dup2(); + m.invokestatic(Type.getInternalName(Double.class), "doubleToRawLongBits", Type.getMethodDescriptor(Type.LONG_TYPE, new Type[]{Type.DOUBLE_TYPE}), false); + m.lconst(9222769054270909007L); + m.lcmp(); + m.ifeq(cacheMiss); + m.areturn(Type.DOUBLE_TYPE); + m.visitLabel(cacheMiss); + m.pop2(); + context.callDelegateSingle(m, delegateMethod); + m.store(eval, Type.DOUBLE_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class)); + m.load(1, Type.INT_TYPE); + m.load(2, Type.INT_TYPE); + m.load(3, Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(eval, Type.DOUBLE_TYPE); + m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$cache", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class), Type.DOUBLE_TYPE})); + m.load(eval, Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(@NotNull Context context, @NotNull InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String delegateMethod = context.newMultiMethod(this.delegate); + String cacheLikeField = context.newField(IFastCacheLike.class, this.cacheLike); + this.genPostprocessingMethod(context, cacheLikeField); + Label cacheExists = new Label(); + Label cacheMiss = new Label(); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class)); + m.ifnonnull(cacheExists); + context.callDelegateMulti(m, delegateMethod); + m.areturn(Type.VOID_TYPE); + m.visitLabel(cacheExists); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class)); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$getCached", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[]{Type.getType(double[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class)})); + m.ifeq(cacheMiss); + m.areturn(Type.VOID_TYPE); + m.visitLabel(cacheMiss); + context.callDelegateMulti(m, delegateMethod); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class)); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$cache", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class)})); + m.areturn(Type.VOID_TYPE); + } + + private void genPostprocessingMethod(@NotNull Context context, String cacheLikeField) { + String methodName = String.format("postProcessing_%s", cacheLikeField); + String delegateSingle = context.newSingleMethod(this.delegate); + String delegateMulti = context.newMultiMethod(this.delegate); + context.genPostprocessingMethod(methodName, (m) -> { + Label cacheExists = new Label(); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class)); + m.dup(); + m.ifnonnull(cacheExists); + m.pop(); + m.pop(); + m.areturn(Type.VOID_TYPE); + m.visitLabel(cacheExists); + m.anew(Type.getType(SubCompiledDensityFunction.class)); + m.dup(); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.invokedynamic("evalSingle", Type.getMethodDescriptor(Type.getType(ISingleMethod.class), new Type[]{Type.getType(context.classDesc)}), new Handle(6, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getMethodType(Context.SINGLE_DESC), new Handle(5, context.className, delegateSingle, Context.SINGLE_DESC, false), Type.getMethodType(Context.SINGLE_DESC)}); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.invokedynamic("evalMulti", Type.getMethodDescriptor(Type.getType(IMultiMethod.class), new Type[]{Type.getType(context.classDesc)}), new Handle(6, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getMethodType(Context.MULTI_DESC), new Handle(5, context.className, delegateMulti, Context.MULTI_DESC, false), Type.getMethodType(Context.MULTI_DESC)}); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class)); + m.checkcast(Type.getType(DensityFunction.class)); + m.invokespecial(Type.getInternalName(SubCompiledDensityFunction.class), "", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(ISingleMethod.class), Type.getType(IMultiMethod.class), Type.getType(DensityFunction.class)}), false); + m.checkcast(Type.getType(DensityFunction.class)); + m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$withDelegate", Type.getMethodDescriptor(Type.getType(DensityFunction.class), new Type[]{Type.getType(DensityFunction.class)})); + m.putfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class)); + m.areturn(Type.VOID_TYPE); + }); + } + + public IFastCacheLike getCacheLike() { + return this.cacheLike; + } + + public AstNode getDelegate() { + return this.delegate; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + CacheLikeNode that = (CacheLikeNode)o; + return equals(this.cacheLike, that.cacheLike) && Objects.equals(this.delegate, that.delegate); + } else { + return false; + } + } + + private static boolean equals(IFastCacheLike a, IFastCacheLike b) { + if (a instanceof DensityFunctions.Marker wrappingA) { + if (b instanceof DensityFunctions.Marker wrappingB) { + return wrappingA.type() == wrappingB.type(); + } + } + + return a.equals(b); + } + + public int hashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + hashCode(this.cacheLike); + result = 31 * result + this.delegate.hashCode(); + return result; + } + + private static int hashCode(IFastCacheLike o) { + if (o instanceof DensityFunctions.Marker wrapping) { + return wrapping.type().hashCode(); + } else { + return o.hashCode(); + } + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + CacheLikeNode that = (CacheLikeNode)o; + return relaxedEquals(this.cacheLike, that.cacheLike) && this.delegate.relaxedEquals(that.delegate); + } else { + return false; + } + } + + private static boolean relaxedEquals(IFastCacheLike a, IFastCacheLike b) { + if (a instanceof DensityFunctions.Marker wrappingA) { + if (b instanceof DensityFunctions.Marker wrappingB) { + return wrappingA.type() == wrappingB.type(); + } + } + + return a.getClass() == b.getClass(); + } + + public int relaxedHashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + relaxedHashCode(this.cacheLike); + result = 31 * result + this.delegate.relaxedHashCode(); + return result; + } + + private static int relaxedHashCode(IFastCacheLike o) { + if (o instanceof DensityFunctions.Marker wrapping) { + return wrapping.type().hashCode(); + } else { + return o.getClass().hashCode(); + } + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/ConstantNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/ConstantNode.java new file mode 100644 index 0000000..558bc3a --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/ConstantNode.java @@ -0,0 +1,72 @@ +package org.bxteam.divinemc.dfc.common.ast.misc; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import java.util.Arrays; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class ConstantNode implements AstNode { + private final double value; + + public ConstantNode(double value) { + this.value = value; + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return this.value; + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + Arrays.fill(res, this.value); + } + + public AstNode[] getChildren() { + return new AstNode[0]; + } + + public AstNode transform(AstTransformer transformer) { + return transformer.transform(this); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + m.dconst(this.value); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.dconst(this.value); + m.invokestatic(Type.getInternalName(Arrays.class), "fill", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class), Type.DOUBLE_TYPE}), false); + m.areturn(Type.VOID_TYPE); + } + + public double getValue() { + return this.value; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + ConstantNode that = (ConstantNode)o; + return Double.compare(this.value, that.value) == 0; + } else { + return false; + } + } + + public int hashCode() { + return Double.hashCode(this.value); + } + + public boolean relaxedEquals(AstNode o) { + return this.equals(o); + } + + public int relaxedHashCode() { + return this.hashCode(); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/DelegateNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/DelegateNode.java new file mode 100644 index 0000000..050d9ff --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/DelegateNode.java @@ -0,0 +1,140 @@ +package org.bxteam.divinemc.dfc.common.ast.misc; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.bxteam.divinemc.dfc.common.util.ArrayCache; +import org.bxteam.divinemc.dfc.common.vif.EachApplierVanillaInterface; +import org.bxteam.divinemc.dfc.common.vif.NoisePosVanillaInterface; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class DelegateNode implements AstNode { + private final DensityFunction densityFunction; + + public DelegateNode(DensityFunction densityFunction) { + this.densityFunction = Objects.requireNonNull(densityFunction); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return this.densityFunction.compute(new NoisePosVanillaInterface(x, y, z, type)); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + if (res.length == 1) { + res[0] = this.evalSingle(x[0], y[0], z[0], type); + } else { + this.densityFunction.fillArray(res, new EachApplierVanillaInterface(x, y, z, type)); + } + } + + public AstNode[] getChildren() { + return new AstNode[0]; + } + + public AstNode transform(AstTransformer transformer) { + return transformer.transform(this); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String newField = context.newField(DensityFunction.class, this.densityFunction); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, newField, Type.getDescriptor(DensityFunction.class)); + m.anew(Type.getType(NoisePosVanillaInterface.class)); + m.dup(); + m.load(1, Type.INT_TYPE); + m.load(2, Type.INT_TYPE); + m.load(3, Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.invokespecial(Type.getInternalName(NoisePosVanillaInterface.class), "", Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class)), false); + m.invokeinterface(Type.getInternalName(DensityFunction.class), "compute", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.getType(DensityFunction.FunctionContext.class))); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String newField = context.newField(DensityFunction.class, this.densityFunction); + Label moreThanTwoLabel = new Label(); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.arraylength(); + m.iconst(1); + m.ificmpgt(moreThanTwoLabel); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.iconst(0); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, newField, Type.getDescriptor(DensityFunction.class)); + m.anew(Type.getType(NoisePosVanillaInterface.class)); + m.dup(); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.iconst(0); + m.aload(Type.INT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.iconst(0); + m.aload(Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.iconst(0); + m.aload(Type.INT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.invokespecial(Type.getInternalName(NoisePosVanillaInterface.class), "", Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class)), false); + m.invokeinterface(Type.getInternalName(DensityFunction.class), "compute", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.getType(DensityFunction.FunctionContext.class))); + m.astore(Type.DOUBLE_TYPE); + m.areturn(Type.VOID_TYPE); + m.visitLabel(moreThanTwoLabel); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, newField, Type.getDescriptor(DensityFunction.class)); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.anew(Type.getType(EachApplierVanillaInterface.class)); + m.dup(); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.invokespecial(Type.getInternalName(EachApplierVanillaInterface.class), "", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class), Type.getType(ArrayCache.class)), false); + m.invokeinterface(Type.getInternalName(DensityFunction.class), "fillArray", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(double[].class), Type.getType(DensityFunction.ContextProvider.class))); + m.areturn(Type.VOID_TYPE); + } + + public DensityFunction getDelegate() { + return this.densityFunction; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + DelegateNode that = (DelegateNode)o; + return Objects.equals(this.densityFunction, that.densityFunction); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + result = 31 * result + Objects.hashCode(this.getClass()); + result = 31 * result + Objects.hashCode(this.densityFunction); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + DelegateNode that = (DelegateNode)o; + return this.densityFunction.getClass() == that.densityFunction.getClass(); + } else { + return false; + } + } + + public int relaxedHashCode() { + int result = 1; + result = 31 * result + Objects.hashCode(this.getClass()); + result = 31 * result + Objects.hashCode(this.densityFunction.getClass()); + return result; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/RangeChoiceNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/RangeChoiceNode.java new file mode 100644 index 0000000..b3ce05b --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/RangeChoiceNode.java @@ -0,0 +1,337 @@ +package org.bxteam.divinemc.dfc.common.ast.misc; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context; +import java.util.Objects; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class RangeChoiceNode implements AstNode { + private final AstNode input; + private final double minInclusive; + private final double maxExclusive; + private final AstNode whenInRange; + private final AstNode whenOutOfRange; + + public RangeChoiceNode(AstNode input, double minInclusive, double maxExclusive, AstNode whenInRange, AstNode whenOutOfRange) { + this.input = (AstNode)Objects.requireNonNull(input); + this.minInclusive = minInclusive; + this.maxExclusive = maxExclusive; + this.whenInRange = (AstNode)Objects.requireNonNull(whenInRange); + this.whenOutOfRange = (AstNode)Objects.requireNonNull(whenOutOfRange); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double v = this.input.evalSingle(x, y, z, type); + return v >= this.minInclusive && v < this.maxExclusive ? this.whenInRange.evalSingle(x, y, z, type) : this.whenOutOfRange.evalSingle(x, y, z, type); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.input.evalMulti(res, x, y, z, type); + int numInRange = 0; + + int numOutOfRange; + for(numOutOfRange = 0; numOutOfRange < x.length; ++numOutOfRange) { + double v = res[numOutOfRange]; + if (v >= this.minInclusive && v < this.maxExclusive) { + ++numInRange; + } + } + + numOutOfRange = res.length - numInRange; + if (numInRange == 0) { + this.evalChildMulti(this.whenOutOfRange, res, x, y, z, type); + } else if (numInRange == res.length) { + this.evalChildMulti(this.whenInRange, res, x, y, z, type); + } else { + int idx1 = 0; + int[] i1 = new int[numInRange]; + double[] res1 = new double[numInRange]; + int[] x1 = new int[numInRange]; + int[] y1 = new int[numInRange]; + int[] z1 = new int[numInRange]; + int idx2 = 0; + int[] i2 = new int[numOutOfRange]; + double[] res2 = new double[numOutOfRange]; + int[] x2 = new int[numOutOfRange]; + int[] y2 = new int[numOutOfRange]; + int[] z2 = new int[numOutOfRange]; + + int i; + for(i = 0; i < res.length; ++i) { + double v = res[i]; + int index; + if (v >= this.minInclusive && v < this.maxExclusive) { + index = idx1++; + i1[index] = i; + x1[index] = x[i]; + y1[index] = y[i]; + z1[index] = z[i]; + } else { + index = idx2++; + i2[index] = i; + x2[index] = x[i]; + y2[index] = y[i]; + z2[index] = z[i]; + } + } + + this.evalChildMulti(this.whenInRange, res1, x1, y1, z1, type); + this.evalChildMulti(this.whenOutOfRange, res2, x2, y2, z2, type); + + for(i = 0; i < numInRange; ++i) { + res[i1[i]] = res1[i]; + } + + for(i = 0; i < numOutOfRange; ++i) { + res[i2[i]] = res2[i]; + } + } + + } + + public static void evalMultiStatic(double[] res, int[] x, int[] y, int[] z, EvalType type, double minInclusive, double maxExclusive, BytecodeGen.EvalSingleInterface whenInRangeSingle, BytecodeGen.EvalSingleInterface whenOutOfRangeSingle, BytecodeGen.EvalMultiInterface inputMulti, BytecodeGen.EvalMultiInterface whenInRangeMulti, BytecodeGen.EvalMultiInterface whenOutOfRangeMulti) { + inputMulti.evalMulti(res, x, y, z, type); + int numInRange = 0; + + int numOutOfRange; + for(numOutOfRange = 0; numOutOfRange < x.length; ++numOutOfRange) { + double v = res[numOutOfRange]; + if (v >= minInclusive && v < maxExclusive) { + ++numInRange; + } + } + + numOutOfRange = res.length - numInRange; + if (numInRange == 0) { + evalChildMulti(whenOutOfRangeSingle, whenOutOfRangeMulti, res, x, y, z, type); + } else if (numInRange == res.length) { + evalChildMulti(whenInRangeSingle, whenInRangeMulti, res, x, y, z, type); + } else { + int idx1 = 0; + int[] i1 = new int[numInRange]; + double[] res1 = new double[numInRange]; + int[] x1 = new int[numInRange]; + int[] y1 = new int[numInRange]; + int[] z1 = new int[numInRange]; + int idx2 = 0; + int[] i2 = new int[numOutOfRange]; + double[] res2 = new double[numOutOfRange]; + int[] x2 = new int[numOutOfRange]; + int[] y2 = new int[numOutOfRange]; + int[] z2 = new int[numOutOfRange]; + + int i; + for(i = 0; i < res.length; ++i) { + double v = res[i]; + int index; + if (v >= minInclusive && v < maxExclusive) { + index = idx1++; + i1[index] = i; + x1[index] = x[i]; + y1[index] = y[i]; + z1[index] = z[i]; + } else { + index = idx2++; + i2[index] = i; + x2[index] = x[i]; + y2[index] = y[i]; + z2[index] = z[i]; + } + } + + evalChildMulti(whenInRangeSingle, whenInRangeMulti, res1, x1, y1, z1, type); + evalChildMulti(whenOutOfRangeSingle, whenOutOfRangeMulti, res2, x2, y2, z2, type); + + for(i = 0; i < numInRange; ++i) { + res[i1[i]] = res1[i]; + } + + for(i = 0; i < numOutOfRange; ++i) { + res[i2[i]] = res2[i]; + } + } + + } + + private static void evalChildMulti(BytecodeGen.EvalSingleInterface single, BytecodeGen.EvalMultiInterface multi, double[] res, int[] x, int[] y, int[] z, EvalType type) { + if (res.length == 1) { + res[0] = single.evalSingle(x[0], y[0], z[0], type); + } else { + multi.evalMulti(res, x, y, z, type); + } + + } + + private void evalChildMulti(AstNode child, double[] res, int[] x, int[] y, int[] z, EvalType type) { + if (res.length == 1) { + res[0] = child.evalSingle(x[0], y[0], z[0], type); + } else { + child.evalMulti(res, x, y, z, type); + } + + } + + public AstNode[] getChildren() { + return new AstNode[]{this.input, this.whenInRange, this.whenOutOfRange}; + } + + public AstNode transform(AstTransformer transformer) { + AstNode input = this.input.transform(transformer); + AstNode whenInRange = this.whenInRange.transform(transformer); + AstNode whenOutOfRange = this.whenOutOfRange.transform(transformer); + return this.input == input && this.whenInRange == whenInRange && this.whenOutOfRange == whenOutOfRange ? transformer.transform(this) : transformer.transform(new RangeChoiceNode(input, this.minInclusive, this.maxExclusive, whenInRange, whenOutOfRange)); + } + + public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String inputMethod = context.newSingleMethod(this.input); + String whenInRangeMethod = context.newSingleMethod(this.whenInRange); + String whenOutOfRangeMethod = context.newSingleMethod(this.whenOutOfRange); + int inputValue = localVarConsumer.createLocalVariable("inputValue", Type.DOUBLE_TYPE.getDescriptor()); + context.callDelegateSingle(m, inputMethod); + m.store(inputValue, Type.DOUBLE_TYPE); + Label whenOutOfRangeLabel = new Label(); + Label end = new Label(); + m.load(inputValue, Type.DOUBLE_TYPE); + m.dconst(this.minInclusive); + m.cmpl(Type.DOUBLE_TYPE); + m.iflt(whenOutOfRangeLabel); + m.load(inputValue, Type.DOUBLE_TYPE); + m.dconst(this.maxExclusive); + m.cmpg(Type.DOUBLE_TYPE); + m.ifge(whenOutOfRangeLabel); + if (whenInRangeMethod.equals(inputMethod)) { + m.load(inputValue, Type.DOUBLE_TYPE); + } else { + context.callDelegateSingle(m, whenInRangeMethod); + } + + m.goTo(end); + m.visitLabel(whenOutOfRangeLabel); + if (whenOutOfRangeMethod.equals(inputMethod)) { + m.load(inputValue, Type.DOUBLE_TYPE); + } else { + context.callDelegateSingle(m, whenOutOfRangeMethod); + } + + m.visitLabel(end); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String inputSingle = context.newSingleMethod(this.input); + String whenInRangeSingle = context.newSingleMethod(this.whenInRange); + String whenOutOfRangeSingle = context.newSingleMethod(this.whenOutOfRange); + String inputMulti = context.newMultiMethod(this.input); + context.callDelegateMulti(m, inputMulti); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + Label whenOutOfRangeLabel = new Label(); + Label end = new Label(); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.dconst(this.minInclusive); + m.cmpl(Type.DOUBLE_TYPE); + m.iflt(whenOutOfRangeLabel); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.dconst(this.maxExclusive); + m.cmpg(Type.DOUBLE_TYPE); + m.ifge(whenOutOfRangeLabel); + if (whenInRangeSingle.equals(inputSingle)) { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + } else { + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, whenInRangeSingle, Context.SINGLE_DESC, false); + } + + m.goTo(end); + m.visitLabel(whenOutOfRangeLabel); + if (whenOutOfRangeSingle.equals(inputSingle)) { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + } else { + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, whenOutOfRangeSingle, Context.SINGLE_DESC, false); + } + + m.visitLabel(end); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + RangeChoiceNode that = (RangeChoiceNode)o; + return Double.compare(this.minInclusive, that.minInclusive) == 0 && Double.compare(this.maxExclusive, that.maxExclusive) == 0 && Objects.equals(this.input, that.input) && Objects.equals(this.whenInRange, that.whenInRange) && Objects.equals(this.whenOutOfRange, that.whenOutOfRange); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.input.hashCode(); + result = 31 * result + Double.hashCode(this.minInclusive); + result = 31 * result + Double.hashCode(this.maxExclusive); + result = 31 * result + this.whenInRange.hashCode(); + result = 31 * result + this.whenOutOfRange.hashCode(); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + RangeChoiceNode that = (RangeChoiceNode)o; + return Double.compare(this.minInclusive, that.minInclusive) == 0 && Double.compare(this.maxExclusive, that.maxExclusive) == 0 && this.input.relaxedEquals(that.input) && this.whenInRange.relaxedEquals(that.whenInRange) && this.whenOutOfRange.relaxedEquals(that.whenOutOfRange); + } else { + return false; + } + } + + public int relaxedHashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.input.relaxedHashCode(); + result = 31 * result + Double.hashCode(this.minInclusive); + result = 31 * result + Double.hashCode(this.maxExclusive); + result = 31 * result + this.whenInRange.relaxedHashCode(); + result = 31 * result + this.whenOutOfRange.relaxedHashCode(); + return result; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/RootNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/RootNode.java new file mode 100644 index 0000000..4c6001f --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/RootNode.java @@ -0,0 +1,82 @@ +package org.bxteam.divinemc.dfc.common.ast.misc; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import java.util.Objects; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class RootNode implements AstNode { + private final AstNode next; + + public RootNode(AstNode next) { + this.next = (AstNode)Objects.requireNonNull(next); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return this.next.evalSingle(x, y, z, type); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.next.evalMulti(res, x, y, z, type); + } + + public AstNode[] getChildren() { + return new AstNode[]{this.next}; + } + + public AstNode transform(AstTransformer transformer) { + AstNode next = this.next.transform(transformer); + return next == this.next ? transformer.transform(this) : transformer.transform(new RootNode(next)); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String nextMethod = context.newSingleMethod(this.next); + context.callDelegateSingle(m, nextMethod); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String nextMethod = context.newMultiMethod(this.next); + context.callDelegateMulti(m, nextMethod); + m.areturn(Type.VOID_TYPE); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + RootNode that = (RootNode)o; + return Objects.equals(this.next, that.next); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.next.hashCode(); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + RootNode that = (RootNode)o; + return this.next.relaxedEquals(that.next); + } else { + return false; + } + } + + public int relaxedHashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.next.relaxedHashCode(); + return result; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/YClampedGradientNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/YClampedGradientNode.java new file mode 100644 index 0000000..0eb4c48 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/misc/YClampedGradientNode.java @@ -0,0 +1,100 @@ +package org.bxteam.divinemc.dfc.common.ast.misc; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import net.minecraft.util.Mth; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class YClampedGradientNode implements AstNode { + private final double fromY; + private final double toY; + private final double fromValue; + private final double toValue; + + public YClampedGradientNode(double fromY, double toY, double fromValue, double toValue) { + this.fromY = fromY; + this.toY = toY; + this.fromValue = fromValue; + this.toValue = toValue; + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return Mth.clampedMap((double)y, this.fromY, this.toY, this.fromValue, this.toValue); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + for(int i = 0; i < res.length; ++i) { + res[i] = Mth.clampedMap((double)y[i], this.fromY, this.toY, this.fromValue, this.toValue); + } + + } + + public AstNode[] getChildren() { + return new AstNode[0]; + } + + public AstNode transform(AstTransformer transformer) { + return transformer.transform(this); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + m.load(2, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.fromY); + m.dconst(this.toY); + m.dconst(this.fromValue); + m.dconst(this.toValue); + m.invokestatic(Type.getInternalName(Mth.class), "clampedMap", "(DDDDD)D", false); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + context.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.fromY); + m.dconst(this.toY); + m.dconst(this.fromValue); + m.dconst(this.toValue); + m.invokestatic(Type.getInternalName(Mth.class), "clampedMap", "(DDDDD)D", false); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + YClampedGradientNode that = (YClampedGradientNode)o; + return Double.compare(this.fromY, that.fromY) == 0 && Double.compare(this.toY, that.toY) == 0 && Double.compare(this.fromValue, that.fromValue) == 0 && Double.compare(this.toValue, that.toValue) == 0; + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + Double.hashCode(this.fromY); + result = 31 * result + Double.hashCode(this.toY); + result = 31 * result + Double.hashCode(this.fromValue); + result = 31 * result + Double.hashCode(this.toValue); + return result; + } + + public boolean relaxedEquals(AstNode o) { + return this.equals(o); + } + + public int relaxedHashCode() { + return this.hashCode(); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTNoiseNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTNoiseNode.java new file mode 100644 index 0000000..76b1899 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTNoiseNode.java @@ -0,0 +1,131 @@ +package org.bxteam.divinemc.dfc.common.ast.noise; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class DFTNoiseNode implements AstNode { + private final DensityFunction.NoiseHolder noise; + private final double xzScale; + private final double yScale; + + public DFTNoiseNode(DensityFunction.NoiseHolder noise, double xzScale, double yScale) { + this.noise = (DensityFunction.NoiseHolder)Objects.requireNonNull(noise); + this.xzScale = xzScale; + this.yScale = yScale; + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return this.noise.getValue((double)x * this.xzScale, (double)y * this.yScale, (double)z * this.xzScale); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + for(int i = 0; i < res.length; ++i) { + res[i] = this.noise.getValue((double)x[i] * this.xzScale, (double)y[i] * this.yScale, (double)z[i] * this.xzScale); + } + + } + + public AstNode[] getChildren() { + return new AstNode[0]; + } + + public AstNode transform(AstTransformer transformer) { + return transformer.transform(this); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(1, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.xzScale); + m.mul(Type.DOUBLE_TYPE); + m.load(2, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.yScale); + m.mul(Type.DOUBLE_TYPE); + m.load(3, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.xzScale); + m.mul(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.xzScale); + m.mul(Type.DOUBLE_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.yScale); + m.mul(Type.DOUBLE_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.xzScale); + m.mul(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + DFTNoiseNode that = (DFTNoiseNode)o; + return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0 && Objects.equals(this.noise, that.noise); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.noise.hashCode(); + result = 31 * result + Double.hashCode(this.xzScale); + result = 31 * result + Double.hashCode(this.yScale); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + DFTNoiseNode that = (DFTNoiseNode)o; + return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0; + } else { + return false; + } + } + + public int relaxedHashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + Double.hashCode(this.xzScale); + result = 31 * result + Double.hashCode(this.yScale); + return result; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTShiftANode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTShiftANode.java new file mode 100644 index 0000000..499c09f --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTShiftANode.java @@ -0,0 +1,115 @@ +package org.bxteam.divinemc.dfc.common.ast.noise; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class DFTShiftANode implements AstNode { + private final DensityFunction.NoiseHolder offsetNoise; + + public DFTShiftANode(DensityFunction.NoiseHolder offsetNoise) { + this.offsetNoise = (DensityFunction.NoiseHolder)Objects.requireNonNull(offsetNoise); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return this.offsetNoise.getValue((double)x * 0.25, 0.0, (double)z * 0.25) * 4.0; + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + for(int i = 0; i < res.length; ++i) { + res[i] = this.offsetNoise.getValue((double)x[i] * 0.25, 0.0, (double)z[i] * 0.25) * 4.0; + } + + } + + public AstNode[] getChildren() { + return new AstNode[0]; + } + + public AstNode transform(AstTransformer transformer) { + return transformer.transform(this); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(1, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.dconst(0.0); + m.load(3, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.dconst(4.0); + m.mul(Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.dconst(0.0); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.dconst(4.0); + m.mul(Type.DOUBLE_TYPE); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + DFTShiftANode that = (DFTShiftANode)o; + return Objects.equals(this.offsetNoise, that.offsetNoise); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + Object o = this.getClass(); + result = 31 * result + o.hashCode(); + result = 31 * result + this.offsetNoise.hashCode(); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else { + return o != null && this.getClass() == o.getClass(); + } + } + + public int relaxedHashCode() { + return this.getClass().hashCode(); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTShiftBNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTShiftBNode.java new file mode 100644 index 0000000..1611540 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTShiftBNode.java @@ -0,0 +1,115 @@ +package org.bxteam.divinemc.dfc.common.ast.noise; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class DFTShiftBNode implements AstNode { + private final DensityFunction.NoiseHolder offsetNoise; + + public DFTShiftBNode(DensityFunction.NoiseHolder offsetNoise) { + this.offsetNoise = (DensityFunction.NoiseHolder)Objects.requireNonNull(offsetNoise); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return this.offsetNoise.getValue((double)z * 0.25, (double)x * 0.25, 0.0) * 4.0; + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + for(int i = 0; i < res.length; ++i) { + res[i] = this.offsetNoise.getValue((double)z[i] * 0.25, (double)x[i] * 0.25, 0.0) * 4.0; + } + + } + + public AstNode[] getChildren() { + return new AstNode[0]; + } + + public AstNode transform(AstTransformer transformer) { + return transformer.transform(this); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(3, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.load(1, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.dconst(0.0); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.dconst(4.0); + m.mul(Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.dconst(0.0); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.dconst(4.0); + m.mul(Type.DOUBLE_TYPE); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + DFTShiftBNode that = (DFTShiftBNode)o; + return Objects.equals(this.offsetNoise, that.offsetNoise); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + Object o = this.getClass(); + result = 31 * result + o.hashCode(); + result = 31 * result + this.offsetNoise.hashCode(); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else { + return o != null && this.getClass() == o.getClass(); + } + } + + public int relaxedHashCode() { + return this.getClass().hashCode(); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTShiftNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTShiftNode.java new file mode 100644 index 0000000..cff0089 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTShiftNode.java @@ -0,0 +1,123 @@ +package org.bxteam.divinemc.dfc.common.ast.noise; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class DFTShiftNode implements AstNode { + private final DensityFunction.NoiseHolder offsetNoise; + + public DFTShiftNode(DensityFunction.NoiseHolder offsetNoise) { + this.offsetNoise = (DensityFunction.NoiseHolder)Objects.requireNonNull(offsetNoise); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return this.offsetNoise.getValue((double)x * 0.25, (double)y * 0.25, (double)z * 0.25) * 4.0; + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + for(int i = 0; i < res.length; ++i) { + res[i] = this.offsetNoise.getValue((double)x[i] * 0.25, (double)y[i] * 0.25, (double)z[i] * 0.25) * 4.0; + } + + } + + public AstNode[] getChildren() { + return new AstNode[0]; + } + + public AstNode transform(AstTransformer transformer) { + return transformer.transform(this); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(1, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.load(2, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.load(3, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.dconst(4.0); + m.mul(Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(0.25); + m.mul(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.dconst(4.0); + m.mul(Type.DOUBLE_TYPE); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + DFTShiftNode that = (DFTShiftNode)o; + return Objects.equals(this.offsetNoise, that.offsetNoise); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + Object o = this.getClass(); + result = 31 * result + o.hashCode(); + result = 31 * result + this.offsetNoise.hashCode(); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else { + return o != null && this.getClass() == o.getClass(); + } + } + + public int relaxedHashCode() { + return this.getClass().hashCode(); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTWeirdScaledSamplerNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTWeirdScaledSamplerNode.java new file mode 100644 index 0000000..9f9c368 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/DFTWeirdScaledSamplerNode.java @@ -0,0 +1,169 @@ +package org.bxteam.divinemc.dfc.common.ast.noise; + +import org.bxteam.divinemc.dfc.common.IDensityFunctionsCaveScaler; +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.DensityFunctions; +import org.jetbrains.annotations.NotNull; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class DFTWeirdScaledSamplerNode implements AstNode { + private final AstNode input; + private final DensityFunction.NoiseHolder noise; + private final DensityFunctions.WeirdScaledSampler.RarityValueMapper mapper; + + public DFTWeirdScaledSamplerNode(AstNode input, DensityFunction.NoiseHolder noise, DensityFunctions.WeirdScaledSampler.RarityValueMapper mapper) { + this.input = Objects.requireNonNull(input); + this.noise = Objects.requireNonNull(noise); + this.mapper = Objects.requireNonNull(mapper); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double v = this.input.evalSingle(x, y, z, type); + double d = (this.mapper.mapper).get(v); + return d * Math.abs(this.noise.getValue((double)x / d, (double)y / d, (double)z / d)); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.input.evalMulti(res, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + double d = (this.mapper.mapper).get(res[i]); + res[i] = d * Math.abs(this.noise.getValue((double)x[i] / d, (double)y[i] / d, (double)z[i] / d)); + } + + } + + public AstNode[] getChildren() { + return new AstNode[]{this.input}; + } + + public AstNode transform(AstTransformer transformer) { + AstNode input = this.input.transform(transformer); + return input == this.input ? transformer.transform(this) : transformer.transform(new DFTWeirdScaledSamplerNode(input, this.noise, this.mapper)); + } + + public void doBytecodeGenSingle(BytecodeGen.@NotNull Context context, InstructionAdapter m, BytecodeGen.Context.@NotNull LocalVarConsumer localVarConsumer) { + String inputMethod = context.newSingleMethod(this.input); + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise); + int scale = localVarConsumer.createLocalVariable("scale", Type.DOUBLE_TYPE.getDescriptor()); + context.callDelegateSingle(m, inputMethod); + switch (this.mapper) { + case TYPE1 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleTunnels", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true); + case TYPE2 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleCaves", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true); + default -> throw new UnsupportedOperationException(String.format("Unknown mapper %s", this.mapper)); + } + + m.store(scale, Type.DOUBLE_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(1, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.load(scale, Type.DOUBLE_TYPE); + m.div(Type.DOUBLE_TYPE); + m.load(2, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.load(scale, Type.DOUBLE_TYPE); + m.div(Type.DOUBLE_TYPE); + m.load(3, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.load(scale, Type.DOUBLE_TYPE); + m.div(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.invokestatic(Type.getInternalName(Math.class), "abs", "(D)D", false); + m.load(scale, Type.DOUBLE_TYPE); + m.mul(Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String inputMethod = context.newMultiMethod(this.input); + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise); + context.callDelegateMulti(m, inputMethod); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + int scale = localVarConsumer.createLocalVariable("scale", Type.DOUBLE_TYPE.getDescriptor()); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + switch (this.mapper) { + case TYPE1 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleTunnels", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true); + case TYPE2 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleCaves", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true); + default -> throw new UnsupportedOperationException(String.format("Unknown mapper %s", this.mapper)); + } + + m.store(scale, Type.DOUBLE_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.load(scale, Type.DOUBLE_TYPE); + m.div(Type.DOUBLE_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.load(scale, Type.DOUBLE_TYPE); + m.div(Type.DOUBLE_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.load(scale, Type.DOUBLE_TYPE); + m.div(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.invokestatic(Type.getInternalName(Math.class), "abs", "(D)D", false); + m.load(scale, Type.DOUBLE_TYPE); + m.mul(Type.DOUBLE_TYPE); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + DFTWeirdScaledSamplerNode that = (DFTWeirdScaledSamplerNode)o; + return Objects.equals(this.input, that.input) && Objects.equals(this.noise, that.noise) && this.mapper == that.mapper; + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.input.hashCode(); + result = 31 * result + this.noise.hashCode(); + result = 31 * result + this.mapper.hashCode(); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + DFTWeirdScaledSamplerNode that = (DFTWeirdScaledSamplerNode)o; + return this.input.relaxedEquals(that.input) && this.mapper == that.mapper; + } else { + return false; + } + } + + public int relaxedHashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.input.relaxedHashCode(); + result = 31 * result + this.mapper.hashCode(); + return result; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/ShiftedNoiseNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/ShiftedNoiseNode.java new file mode 100644 index 0000000..75c82a6 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/noise/ShiftedNoiseNode.java @@ -0,0 +1,215 @@ +package org.bxteam.divinemc.dfc.common.ast.noise; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context; +import org.bxteam.divinemc.dfc.common.util.ArrayCache; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class ShiftedNoiseNode implements AstNode { + private final AstNode shiftX; + private final AstNode shiftY; + private final AstNode shiftZ; + private final double xzScale; + private final double yScale; + private final DensityFunction.NoiseHolder noise; + + public ShiftedNoiseNode(AstNode shiftX, AstNode shiftY, AstNode shiftZ, double xzScale, double yScale, DensityFunction.NoiseHolder noise) { + this.shiftX = (AstNode)Objects.requireNonNull(shiftX); + this.shiftY = (AstNode)Objects.requireNonNull(shiftY); + this.shiftZ = (AstNode)Objects.requireNonNull(shiftZ); + this.xzScale = xzScale; + this.yScale = yScale; + this.noise = (DensityFunction.NoiseHolder)Objects.requireNonNull(noise); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double d = (double)x * this.xzScale + this.shiftX.evalSingle(x, y, z, type); + double e = (double)y * this.yScale + this.shiftY.evalSingle(x, y, z, type); + double f = (double)z * this.xzScale + this.shiftZ.evalSingle(x, y, z, type); + return this.noise.getValue(d, e, f); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + double[] res1 = new double[res.length]; + double[] res2 = new double[res.length]; + this.shiftX.evalMulti(res, x, y, z, type); + this.shiftY.evalMulti(res1, x, y, z, type); + this.shiftZ.evalMulti(res2, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + res[i] = this.noise.getValue((double)x[i] * this.xzScale + res[i], (double)y[i] * this.yScale + res1[i], (double)z[i] * this.xzScale + res2[i]); + } + + } + + public AstNode[] getChildren() { + return new AstNode[]{this.shiftX, this.shiftY, this.shiftZ}; + } + + public AstNode transform(AstTransformer transformer) { + AstNode shiftX = this.shiftX.transform(transformer); + AstNode shiftY = this.shiftY.transform(transformer); + AstNode shiftZ = this.shiftZ.transform(transformer); + return shiftX == this.shiftX && shiftY == this.shiftY && shiftZ == this.shiftZ ? transformer.transform(this) : transformer.transform(new ShiftedNoiseNode(shiftX, shiftY, shiftZ, this.xzScale, this.yScale, this.noise)); + } + + public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise); + String shiftXMethod = context.newSingleMethod(this.shiftX); + String shiftYMethod = context.newSingleMethod(this.shiftY); + String shiftZMethod = context.newSingleMethod(this.shiftZ); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(1, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.xzScale); + m.mul(Type.DOUBLE_TYPE); + context.callDelegateSingle(m, shiftXMethod); + m.add(Type.DOUBLE_TYPE); + m.load(2, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.yScale); + m.mul(Type.DOUBLE_TYPE); + context.callDelegateSingle(m, shiftYMethod); + m.add(Type.DOUBLE_TYPE); + m.load(3, Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.xzScale); + m.mul(Type.DOUBLE_TYPE); + context.callDelegateSingle(m, shiftZMethod); + m.add(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) { + String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise); + String shiftXMethod = context.newMultiMethod(this.shiftX); + String shiftYMethod = context.newMultiMethod(this.shiftY); + String shiftZMethod = context.newMultiMethod(this.shiftZ); + int res1 = localVarConsumer.createLocalVariable("res1", Type.getDescriptor(double[].class)); + int res2 = localVarConsumer.createLocalVariable("res2", Type.getDescriptor(double[].class)); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.arraylength(); + m.iconst(0); + m.invokevirtual(Type.getInternalName(ArrayCache.class), "getDoubleArray", Type.getMethodDescriptor(Type.getType(double[].class), new Type[]{Type.INT_TYPE, Type.BOOLEAN_TYPE}), false); + m.store(res1, InstructionAdapter.OBJECT_TYPE); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.arraylength(); + m.iconst(0); + m.invokevirtual(Type.getInternalName(ArrayCache.class), "getDoubleArray", Type.getMethodDescriptor(Type.getType(double[].class), new Type[]{Type.INT_TYPE, Type.BOOLEAN_TYPE}), false); + m.store(res2, InstructionAdapter.OBJECT_TYPE); + context.callDelegateMulti(m, shiftXMethod); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(res1, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, shiftYMethod, Context.MULTI_DESC, false); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(res2, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, shiftZMethod, Context.MULTI_DESC, false); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class)); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.xzScale); + m.mul(Type.DOUBLE_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.add(Type.DOUBLE_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.yScale); + m.mul(Type.DOUBLE_TYPE); + m.load(res1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.add(Type.DOUBLE_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE); + m.dconst(this.xzScale); + m.mul(Type.DOUBLE_TYPE); + m.load(res2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.DOUBLE_TYPE); + m.add(Type.DOUBLE_TYPE); + m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false); + m.astore(Type.DOUBLE_TYPE); + }); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.load(res1, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(Type.getInternalName(ArrayCache.class), "recycle", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class)}), false); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.load(res2, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(Type.getInternalName(ArrayCache.class), "recycle", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class)}), false); + m.areturn(Type.VOID_TYPE); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + ShiftedNoiseNode that = (ShiftedNoiseNode)o; + return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0 && Objects.equals(this.shiftX, that.shiftX) && Objects.equals(this.shiftY, that.shiftY) && Objects.equals(this.shiftZ, that.shiftZ) && Objects.equals(this.noise, that.noise); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + result = 31 * result + this.shiftX.hashCode(); + result = 31 * result + this.shiftY.hashCode(); + result = 31 * result + this.shiftZ.hashCode(); + result = 31 * result + Double.hashCode(this.xzScale); + result = 31 * result + Double.hashCode(this.yScale); + result = 31 * result + this.noise.hashCode(); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + ShiftedNoiseNode that = (ShiftedNoiseNode)o; + return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0 && this.shiftX.relaxedEquals(that.shiftX) && this.shiftY.relaxedEquals(that.shiftY) && this.shiftZ.relaxedEquals(that.shiftZ); + } else { + return false; + } + } + + public int relaxedHashCode() { + int result = 1; + result = 31 * result + this.shiftX.relaxedHashCode(); + result = 31 * result + this.shiftY.relaxedHashCode(); + result = 31 * result + this.shiftZ.relaxedHashCode(); + result = 31 * result + Double.hashCode(this.xzScale); + result = 31 * result + Double.hashCode(this.yScale); + return result; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/spline/SplineAstNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/spline/SplineAstNode.java new file mode 100644 index 0000000..176b6a1 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/spline/SplineAstNode.java @@ -0,0 +1,453 @@ +package org.bxteam.divinemc.dfc.common.ast.spline; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.ast.McToAst; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.bxteam.divinemc.dfc.common.vif.NoisePosVanillaInterface; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.IntObjectPair; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import net.minecraft.util.CubicSpline; +import net.minecraft.util.Mth; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.DensityFunctions; +import org.bxteam.divinemc.util.Assertions; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.AnalyzerAdapter; +import org.objectweb.asm.commons.InstructionAdapter; + +public class SplineAstNode implements AstNode { + public static final String SPLINE_METHOD_DESC; + private final CubicSpline spline; + + public SplineAstNode(CubicSpline spline) { + this.spline = spline; + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return (double)this.spline.apply(new DensityFunctions.Spline.Point(new NoisePosVanillaInterface(x, y, z, type))); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + for(int i = 0; i < res.length; ++i) { + res[i] = this.evalSingle(x[i], y[i], z[i], type); + } + + } + + public AstNode[] getChildren() { + return new AstNode[0]; + } + + public AstNode transform(AstTransformer transformer) { + return transformer.transform(this); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + ValuesMethodDef splineMethod = doBytecodeGenSpline(context, this.spline); + callSplineSingle(context, m, splineMethod); + m.cast(Type.FLOAT_TYPE, Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + private static ValuesMethodDef doBytecodeGenSpline(BytecodeGen.Context context, CubicSpline spline) { + String name = context.getCachedSplineMethod(spline); + if (name != null) { + return new ValuesMethodDef(false, name, 0.0F); + } else if (spline instanceof CubicSpline.Constant) { + CubicSpline.Constant spline1 = (CubicSpline.Constant)spline; + return new ValuesMethodDef(true, (String)null, spline1.value()); + } else { + name = context.nextMethodName("Spline"); + InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 18, name, SPLINE_METHOD_DESC, context.classWriter.visitMethod(18, name, SPLINE_METHOD_DESC, (String)null, (String[])null))); + List>> extraLocals = new ArrayList(); + Label start = new Label(); + Label end = new Label(); + m.visitLabel(start); + BytecodeGen.Context.LocalVarConsumer localVarConsumer = (localName, localDesc) -> { + int ordinal = extraLocals.size() + 5; + extraLocals.add(IntObjectPair.of(ordinal, Pair.of(localName, localDesc))); + return ordinal; + }; + if (spline instanceof CubicSpline.Multipoint) { + CubicSpline.Multipoint impl = (CubicSpline.Multipoint)spline; + ValuesMethodDef[] valuesMethods = (ValuesMethodDef[])impl.values().stream().map((spline1x) -> { + return doBytecodeGenSpline(context, spline1x); + }).toArray((x$0) -> { + return new ValuesMethodDef[x$0]; + }); + String locations = context.newField(float[].class, impl.locations()); + String derivatives = context.newField(float[].class, impl.derivatives()); + int point = localVarConsumer.createLocalVariable("point", Type.FLOAT_TYPE.getDescriptor()); + int rangeForLocation = localVarConsumer.createLocalVariable("rangeForLocation", Type.INT_TYPE.getDescriptor()); + int lastConst = impl.locations().length - 1; + String locationFunction = context.newSingleMethod(McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)impl.coordinate()).function().value())); + context.callDelegateSingle(m, locationFunction); + m.cast(Type.DOUBLE_TYPE, Type.FLOAT_TYPE); + m.store(point, Type.FLOAT_TYPE); + if (valuesMethods.length == 1) { + m.load(point, Type.FLOAT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, locations, Type.getDescriptor(float[].class)); + callSplineSingle(context, m, valuesMethods[0]); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, derivatives, Type.getDescriptor(float[].class)); + m.iconst(0); + m.invokestatic(Type.getInternalName(SplineSupport.class), "sampleOutsideRange", Type.getMethodDescriptor(Type.FLOAT_TYPE, new Type[]{Type.FLOAT_TYPE, Type.getType(float[].class), Type.FLOAT_TYPE, Type.getType(float[].class), Type.INT_TYPE}), false); + m.areturn(Type.FLOAT_TYPE); + } else { + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, locations, Type.getDescriptor(float[].class)); + m.load(point, Type.FLOAT_TYPE); + m.invokestatic(Type.getInternalName(SplineSupport.class), "findRangeForLocation", Type.getMethodDescriptor(Type.INT_TYPE, new Type[]{Type.getType(float[].class), Type.FLOAT_TYPE}), false); + m.store(rangeForLocation, Type.INT_TYPE); + Label label1 = new Label(); + Label label2 = new Label(); + m.load(rangeForLocation, Type.INT_TYPE); + m.ifge(label1); + m.load(point, Type.FLOAT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, locations, Type.getDescriptor(float[].class)); + callSplineSingle(context, m, valuesMethods[0]); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, derivatives, Type.getDescriptor(float[].class)); + m.iconst(0); + m.invokestatic(Type.getInternalName(SplineSupport.class), "sampleOutsideRange", Type.getMethodDescriptor(Type.FLOAT_TYPE, new Type[]{Type.FLOAT_TYPE, Type.getType(float[].class), Type.FLOAT_TYPE, Type.getType(float[].class), Type.INT_TYPE}), false); + m.areturn(Type.FLOAT_TYPE); + m.visitLabel(label1); + m.load(rangeForLocation, Type.INT_TYPE); + m.iconst(lastConst); + m.ificmpne(label2); + m.load(point, Type.FLOAT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, locations, Type.getDescriptor(float[].class)); + callSplineSingle(context, m, valuesMethods[lastConst]); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, derivatives, Type.getDescriptor(float[].class)); + m.iconst(lastConst); + m.invokestatic(Type.getInternalName(SplineSupport.class), "sampleOutsideRange", Type.getMethodDescriptor(Type.FLOAT_TYPE, new Type[]{Type.FLOAT_TYPE, Type.getType(float[].class), Type.FLOAT_TYPE, Type.getType(float[].class), Type.INT_TYPE}), false); + m.areturn(Type.FLOAT_TYPE); + m.visitLabel(label2); + int loc0 = localVarConsumer.createLocalVariable("loc0", Type.FLOAT_TYPE.getDescriptor()); + int loc1 = localVarConsumer.createLocalVariable("loc1", Type.FLOAT_TYPE.getDescriptor()); + int locDist = localVarConsumer.createLocalVariable("locDist", Type.FLOAT_TYPE.getDescriptor()); + int k = localVarConsumer.createLocalVariable("k", Type.FLOAT_TYPE.getDescriptor()); + int n = localVarConsumer.createLocalVariable("n", Type.FLOAT_TYPE.getDescriptor()); + int o = localVarConsumer.createLocalVariable("o", Type.FLOAT_TYPE.getDescriptor()); + int onDist = localVarConsumer.createLocalVariable("onDist", Type.FLOAT_TYPE.getDescriptor()); + int p = localVarConsumer.createLocalVariable("p", Type.FLOAT_TYPE.getDescriptor()); + int q = localVarConsumer.createLocalVariable("q", Type.FLOAT_TYPE.getDescriptor()); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, locations, Type.getDescriptor(float[].class)); + m.load(rangeForLocation, Type.INT_TYPE); + m.aload(Type.FLOAT_TYPE); + m.store(loc0, Type.FLOAT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, locations, Type.getDescriptor(float[].class)); + m.load(rangeForLocation, Type.INT_TYPE); + m.iconst(1); + m.add(Type.INT_TYPE); + m.aload(Type.FLOAT_TYPE); + m.store(loc1, Type.FLOAT_TYPE); + m.load(loc1, Type.FLOAT_TYPE); + m.load(loc0, Type.FLOAT_TYPE); + m.sub(Type.FLOAT_TYPE); + m.store(locDist, Type.FLOAT_TYPE); + m.load(point, Type.FLOAT_TYPE); + m.load(loc0, Type.FLOAT_TYPE); + m.sub(Type.FLOAT_TYPE); + m.load(locDist, Type.FLOAT_TYPE); + m.div(Type.FLOAT_TYPE); + m.store(k, Type.FLOAT_TYPE); + Label[] jumpLabels = new Label[valuesMethods.length - 1]; + boolean[] jumpGenerated = new boolean[valuesMethods.length - 1]; + + for(int i = 0; i < valuesMethods.length - 1; ++i) { + jumpLabels[i] = new Label(); + } + + Label defaultLabel = new Label(); + Label label3 = new Label(); + m.load(rangeForLocation, Type.INT_TYPE); + m.tableswitch(0, valuesMethods.length - 2, defaultLabel, jumpLabels); + + for(int i = 0; i < valuesMethods.length - 1; ++i) { + if (!jumpGenerated[i]) { + m.visitLabel(jumpLabels[i]); + jumpGenerated[i] = true; + + for(int j = i + 1; j < valuesMethods.length - 1; ++j) { + if (valuesMethods[i].equals(valuesMethods[j]) && valuesMethods[i + 1].equals(valuesMethods[j + 1])) { + m.visitLabel(jumpLabels[j]); + jumpGenerated[j] = true; + } + } + + callSplineSingle(context, m, valuesMethods[i]); + if (valuesMethods[i].equals(valuesMethods[i + 1])) { + m.dup(); + m.store(n, Type.FLOAT_TYPE); + m.store(o, Type.FLOAT_TYPE); + } else { + m.store(n, Type.FLOAT_TYPE); + callSplineSingle(context, m, valuesMethods[i + 1]); + m.store(o, Type.FLOAT_TYPE); + } + + m.goTo(label3); + } + } + + m.visitLabel(defaultLabel); + m.iconst(0); + m.aconst("boom"); + m.invokestatic(Type.getInternalName(Assertions.class), "assertTrue", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.BOOLEAN_TYPE, Type.getType(String.class)}), false); + m.fconst(Float.NaN); + m.areturn(Type.FLOAT_TYPE); + m.visitLabel(label3); + m.load(o, Type.FLOAT_TYPE); + m.load(n, Type.FLOAT_TYPE); + m.sub(Type.FLOAT_TYPE); + m.store(onDist, Type.FLOAT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, derivatives, Type.getDescriptor(float[].class)); + m.load(rangeForLocation, Type.INT_TYPE); + m.aload(Type.FLOAT_TYPE); + m.load(locDist, Type.FLOAT_TYPE); + m.mul(Type.FLOAT_TYPE); + m.load(onDist, Type.FLOAT_TYPE); + m.sub(Type.FLOAT_TYPE); + m.store(p, Type.FLOAT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, derivatives, Type.getDescriptor(float[].class)); + m.load(rangeForLocation, Type.INT_TYPE); + m.iconst(1); + m.add(Type.INT_TYPE); + m.aload(Type.FLOAT_TYPE); + m.neg(Type.FLOAT_TYPE); + m.load(locDist, Type.FLOAT_TYPE); + m.mul(Type.FLOAT_TYPE); + m.load(onDist, Type.FLOAT_TYPE); + m.add(Type.FLOAT_TYPE); + m.store(q, Type.FLOAT_TYPE); + m.load(k, Type.FLOAT_TYPE); + m.load(n, Type.FLOAT_TYPE); + m.load(o, Type.FLOAT_TYPE); + m.invokestatic(Type.getInternalName(Mth.class), "lerp", "(FFF)F", false); + m.load(k, Type.FLOAT_TYPE); + m.fconst(1.0F); + m.load(k, Type.FLOAT_TYPE); + m.sub(Type.FLOAT_TYPE); + m.mul(Type.FLOAT_TYPE); + m.load(k, Type.FLOAT_TYPE); + m.load(p, Type.FLOAT_TYPE); + m.load(q, Type.FLOAT_TYPE); + m.invokestatic(Type.getInternalName(Mth.class), "lerp", "(FFF)F", false); + m.mul(Type.FLOAT_TYPE); + m.add(Type.FLOAT_TYPE); + m.areturn(Type.FLOAT_TYPE); + } + } else { + if (!(spline instanceof CubicSpline.Constant)) { + throw new UnsupportedOperationException(String.format("Unsupported spline implementation: %s", spline.getClass().getName())); + } + + CubicSpline.Constant floatFunction = (CubicSpline.Constant)spline; + m.fconst(floatFunction.value()); + m.areturn(Type.FLOAT_TYPE); + } + + m.visitLabel(end); + m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0); + m.visitLocalVariable("x", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 1); + m.visitLocalVariable("y", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 2); + m.visitLocalVariable("z", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 3); + m.visitLocalVariable("evalType", Type.getType(EvalType.class).getDescriptor(), (String)null, start, end, 4); + Iterator var35 = extraLocals.iterator(); + + while(var35.hasNext()) { + IntObjectPair> local = (IntObjectPair)var35.next(); + m.visitLocalVariable((String)((Pair)local.right()).left(), (String)((Pair)local.right()).right(), (String)null, start, end, local.leftInt()); + } + + m.visitMaxs(0, 0); + context.cacheSplineMethod(spline, name); + return new ValuesMethodDef(false, name, 0.0F); + } + } + + private static void callSplineSingle(BytecodeGen.Context context, InstructionAdapter m, ValuesMethodDef target) { + if (target.isConst()) { + m.fconst(target.constValue()); + } else { + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(1, Type.INT_TYPE); + m.load(2, Type.INT_TYPE); + m.load(3, Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, target.generatedMethod(), SPLINE_METHOD_DESC, false); + } + + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + context.delegateToSingle(m, localVarConsumer, this); + m.areturn(Type.VOID_TYPE); + } + + private static boolean deepEquals(CubicSpline a, CubicSpline b) { + if (a instanceof CubicSpline.Constant a1) { + if (b instanceof CubicSpline.Constant b1) { + return a1.value() == b1.value(); + } + } + + if (a instanceof CubicSpline.Multipoint a1) { + if (b instanceof CubicSpline.Multipoint b1) { + boolean equals1 = Arrays.equals(a1.derivatives(), b1.derivatives()) && Arrays.equals(a1.locations(), b1.locations()) && a1.values().size() == b1.values().size() && McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).equals(McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)b1.coordinate()).function().value())); + if (!equals1) { + return false; + } + + int size = a1.values().size(); + + for(int i = 0; i < size; ++i) { + if (!deepEquals((CubicSpline)a1.values().get(i), (CubicSpline)b1.values().get(i))) { + return false; + } + } + + return true; + } + } + + return false; + } + + private static boolean deepRelaxedEquals(CubicSpline a, CubicSpline b) { + if (a instanceof CubicSpline.Constant a1) { + if (b instanceof CubicSpline.Constant b1) { + return a1.value() == b1.value(); + } + } + + if (a instanceof CubicSpline.Multipoint a1) { + if (b instanceof CubicSpline.Multipoint b1) { + boolean equals1 = a1.values().size() == b1.values().size() && McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).relaxedEquals(McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)b1.coordinate()).function().value())); + if (!equals1) { + return false; + } + + int size = a1.values().size(); + + for(int i = 0; i < size; ++i) { + if (!deepRelaxedEquals((CubicSpline)a1.values().get(i), (CubicSpline)b1.values().get(i))) { + return false; + } + } + + return true; + } + } + + return false; + } + + private static int deepHashcode(CubicSpline a) { + if (a instanceof CubicSpline.Constant a1) { + return Float.hashCode(a1.value()); + } else if (!(a instanceof CubicSpline.Multipoint a1)) { + return a.hashCode(); + } else { + int result = 1; + result = 31 * result + Arrays.hashCode(a1.derivatives()); + result = 31 * result + Arrays.hashCode(a1.locations()); + + CubicSpline spline; + for(Iterator var4 = a1.values().iterator(); var4.hasNext(); result = 31 * result + deepHashcode(spline)) { + spline = (CubicSpline)var4.next(); + } + + result = 31 * result + McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).hashCode(); + return result; + } + } + + private static int deepRelaxedHashcode(CubicSpline a) { + if (a instanceof CubicSpline.Constant a1) { + return Float.hashCode(a1.value()); + } else if (!(a instanceof CubicSpline.Multipoint a1)) { + return a.hashCode(); + } else { + int result = 1; + + CubicSpline spline; + for(Iterator var4 = a1.values().iterator(); var4.hasNext(); result = 31 * result + deepRelaxedHashcode(spline)) { + spline = (CubicSpline)var4.next(); + } + + result = 31 * result + McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).relaxedHashCode(); + return result; + } + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + SplineAstNode that = (SplineAstNode)o; + return deepEquals(this.spline, that.spline); + } else { + return false; + } + } + + public int hashCode() { + return deepHashcode(this.spline); + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + SplineAstNode that = (SplineAstNode)o; + return deepRelaxedEquals(this.spline, that.spline); + } else { + return false; + } + } + + public int relaxedHashCode() { + return deepRelaxedHashcode(this.spline); + } + + static { + SPLINE_METHOD_DESC = Type.getMethodDescriptor(Type.getType(Float.TYPE), new Type[]{Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(EvalType.class)}); + } + + private static record ValuesMethodDef(boolean isConst, String generatedMethod, float constValue) { + private ValuesMethodDef(boolean isConst, String generatedMethod, float constValue) { + this.isConst = isConst; + this.generatedMethod = generatedMethod; + this.constValue = constValue; + } + + public boolean isConst() { + return this.isConst; + } + + public String generatedMethod() { + return this.generatedMethod; + } + + public float constValue() { + return this.constValue; + } + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/spline/SplineSupport.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/spline/SplineSupport.java new file mode 100644 index 0000000..ee23110 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/spline/SplineSupport.java @@ -0,0 +1,29 @@ +package org.bxteam.divinemc.dfc.common.ast.spline; + +public class SplineSupport { + public SplineSupport() { + } + + public static int findRangeForLocation(float[] locations, float x) { + int min = 0; + int i = locations.length; + + while(i > 0) { + int j = i / 2; + int k = min + j; + if (x < locations[k]) { + i = j; + } else { + min = k + 1; + i -= j + 1; + } + } + + return min - 1; + } + + public static float sampleOutsideRange(float point, float[] locations, float value, float[] derivatives, int i) { + float f = derivatives[i]; + return f == 0.0F ? value : value + f * (point - locations[i]); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/AbsNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/AbsNode.java new file mode 100644 index 0000000..e3a91a5 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/AbsNode.java @@ -0,0 +1,49 @@ +package org.bxteam.divinemc.dfc.common.ast.unary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class AbsNode extends AbstractUnaryNode { + public AbsNode(AstNode operand) { + super(operand); + } + + protected AstNode newInstance(AstNode operand) { + return new AbsNode(operand); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + return Math.abs(this.operand.evalSingle(x, y, z, type)); + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.operand.evalMulti(res, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + res[i] = Math.abs(res[i]); + } + + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenSingle(context, m, localVarConsumer); + m.invokestatic(Type.getInternalName(Math.class), "abs", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE}), false); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenMulti(context, m, localVarConsumer); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.dup2(); + m.aload(Type.DOUBLE_TYPE); + m.invokestatic(Type.getInternalName(Math.class), "abs", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE}), false); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/AbstractUnaryNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/AbstractUnaryNode.java new file mode 100644 index 0000000..a48ca64 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/AbstractUnaryNode.java @@ -0,0 +1,72 @@ +package org.bxteam.divinemc.dfc.common.ast.unary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.AstTransformer; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import java.util.Objects; +import org.objectweb.asm.commons.InstructionAdapter; + +public abstract class AbstractUnaryNode implements AstNode { + protected final AstNode operand; + + public AbstractUnaryNode(AstNode operand) { + this.operand = (AstNode)Objects.requireNonNull(operand); + } + + public AstNode[] getChildren() { + return new AstNode[]{this.operand}; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + AbstractUnaryNode that = (AbstractUnaryNode)o; + return Objects.equals(this.operand, that.operand); + } else { + return false; + } + } + + public int hashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.operand.hashCode(); + return result; + } + + public boolean relaxedEquals(AstNode o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + AbstractUnaryNode that = (AbstractUnaryNode)o; + return this.operand.relaxedEquals(that.operand); + } else { + return false; + } + } + + public int relaxedHashCode() { + int result = 1; + result = 31 * result + this.getClass().hashCode(); + result = 31 * result + this.operand.relaxedHashCode(); + return result; + } + + protected abstract AstNode newInstance(AstNode var1); + + public AstNode transform(AstTransformer transformer) { + AstNode operand = this.operand.transform(transformer); + return this.operand == operand ? transformer.transform(this) : transformer.transform(this.newInstance(operand)); + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String operandMethod = context.newSingleMethod(this.operand); + context.callDelegateSingle(m, operandMethod); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String operandMethod = context.newMultiMethod(this.operand); + context.callDelegateMulti(m, operandMethod); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/CubeNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/CubeNode.java new file mode 100644 index 0000000..8bd4d3f --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/CubeNode.java @@ -0,0 +1,56 @@ +package org.bxteam.divinemc.dfc.common.ast.unary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class CubeNode extends AbstractUnaryNode { + public CubeNode(AstNode operand) { + super(operand); + } + + protected AstNode newInstance(AstNode operand) { + return new CubeNode(operand); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double v = this.operand.evalSingle(x, y, z, type); + return v * v * v; + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.operand.evalMulti(res, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + res[i] = res[i] * res[i] * res[i]; + } + + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenSingle(context, m, localVarConsumer); + m.dup2(); + m.dup2(); + m.mul(Type.DOUBLE_TYPE); + m.mul(Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenMulti(context, m, localVarConsumer); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.dup2(); + m.aload(Type.DOUBLE_TYPE); + m.dup2(); + m.dup2(); + m.mul(Type.DOUBLE_TYPE); + m.mul(Type.DOUBLE_TYPE); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/NegMulNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/NegMulNode.java new file mode 100644 index 0000000..4be770d --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/NegMulNode.java @@ -0,0 +1,83 @@ +package org.bxteam.divinemc.dfc.common.ast.unary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class NegMulNode extends AbstractUnaryNode { + private final double negMul; + + public NegMulNode(AstNode operand, double negMul) { + super(operand); + this.negMul = negMul; + } + + protected AstNode newInstance(AstNode operand) { + return new NegMulNode(operand, this.negMul); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double v = this.operand.evalSingle(x, y, z, type); + return v > 0.0 ? v : v * this.negMul; + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.operand.evalMulti(res, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + double v = res[i]; + res[i] = v > 0.0 ? v : v * this.negMul; + } + + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenSingle(context, m, localVarConsumer); + int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor()); + m.store(v, Type.DOUBLE_TYPE); + Label negMulLabel = new Label(); + Label end = new Label(); + m.load(v, Type.DOUBLE_TYPE); + m.dconst(0.0); + m.cmpl(Type.DOUBLE_TYPE); + m.ifle(negMulLabel); + m.load(v, Type.DOUBLE_TYPE); + m.goTo(end); + m.visitLabel(negMulLabel); + m.load(v, Type.DOUBLE_TYPE); + m.dconst(this.negMul); + m.mul(Type.DOUBLE_TYPE); + m.visitLabel(end); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenMulti(context, m, localVarConsumer); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor()); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.dup2(); + m.aload(Type.DOUBLE_TYPE); + m.store(v, Type.DOUBLE_TYPE); + Label negMulLabel = new Label(); + Label end = new Label(); + m.load(v, Type.DOUBLE_TYPE); + m.dconst(0.0); + m.cmpl(Type.DOUBLE_TYPE); + m.ifle(negMulLabel); + m.load(v, Type.DOUBLE_TYPE); + m.goTo(end); + m.visitLabel(negMulLabel); + m.load(v, Type.DOUBLE_TYPE); + m.dconst(this.negMul); + m.mul(Type.DOUBLE_TYPE); + m.visitLabel(end); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/SquareNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/SquareNode.java new file mode 100644 index 0000000..284ff98 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/SquareNode.java @@ -0,0 +1,52 @@ +package org.bxteam.divinemc.dfc.common.ast.unary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class SquareNode extends AbstractUnaryNode { + public SquareNode(AstNode operand) { + super(operand); + } + + protected AstNode newInstance(AstNode operand) { + return new SquareNode(operand); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double v = this.operand.evalSingle(x, y, z, type); + return v * v; + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.operand.evalMulti(res, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + res[i] *= res[i]; + } + + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenSingle(context, m, localVarConsumer); + m.dup2(); + m.mul(Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenMulti(context, m, localVarConsumer); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.dup2(); + m.aload(Type.DOUBLE_TYPE); + m.dup2(); + m.mul(Type.DOUBLE_TYPE); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/SqueezeNode.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/SqueezeNode.java new file mode 100644 index 0000000..ae7e8b1 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ast/unary/SqueezeNode.java @@ -0,0 +1,84 @@ +package org.bxteam.divinemc.dfc.common.ast.unary; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.gen.BytecodeGen; +import net.minecraft.util.Mth; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class SqueezeNode extends AbstractUnaryNode { + public SqueezeNode(AstNode operand) { + super(operand); + } + + protected AstNode newInstance(AstNode operand) { + return new SqueezeNode(operand); + } + + public double evalSingle(int x, int y, int z, EvalType type) { + double v = Mth.clamp(this.operand.evalSingle(x, y, z, type), -1.0, 1.0); + return v / 2.0 - v * v * v / 24.0; + } + + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.operand.evalMulti(res, x, y, z, type); + + for(int i = 0; i < res.length; ++i) { + double v = Mth.clamp(res[i], -1.0, 1.0); + res[i] = v / 2.0 - v * v * v / 24.0; + } + + } + + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenSingle(context, m, localVarConsumer); + m.dconst(1.0); + m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.dconst(-1.0); + m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor()); + m.store(v, Type.DOUBLE_TYPE); + m.load(v, Type.DOUBLE_TYPE); + m.dconst(2.0); + m.div(Type.DOUBLE_TYPE); + m.load(v, Type.DOUBLE_TYPE); + m.dup2(); + m.dup2(); + m.mul(Type.DOUBLE_TYPE); + m.mul(Type.DOUBLE_TYPE); + m.dconst(24.0); + m.div(Type.DOUBLE_TYPE); + m.sub(Type.DOUBLE_TYPE); + m.areturn(Type.DOUBLE_TYPE); + } + + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + super.doBytecodeGenMulti(context, m, localVarConsumer); + context.doCountedLoop(m, localVarConsumer, (idx) -> { + int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor()); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.dup2(); + m.aload(Type.DOUBLE_TYPE); + m.dconst(1.0); + m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.dconst(-1.0); + m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false); + m.store(v, Type.DOUBLE_TYPE); + m.load(v, Type.DOUBLE_TYPE); + m.dconst(2.0); + m.div(Type.DOUBLE_TYPE); + m.load(v, Type.DOUBLE_TYPE); + m.dup2(); + m.dup2(); + m.mul(Type.DOUBLE_TYPE); + m.mul(Type.DOUBLE_TYPE); + m.dconst(24.0); + m.div(Type.DOUBLE_TYPE); + m.sub(Type.DOUBLE_TYPE); + m.astore(Type.DOUBLE_TYPE); + }); + m.areturn(Type.VOID_TYPE); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IArrayCacheCapable.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IArrayCacheCapable.java new file mode 100644 index 0000000..2afddfa --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IArrayCacheCapable.java @@ -0,0 +1,7 @@ +package org.bxteam.divinemc.dfc.common.ducks; + +import org.bxteam.divinemc.dfc.common.util.ArrayCache; + +public interface IArrayCacheCapable { + ArrayCache c2me$getArrayCache(); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IBlendingAwareVisitor.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IBlendingAwareVisitor.java new file mode 100644 index 0000000..1454273 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IBlendingAwareVisitor.java @@ -0,0 +1,5 @@ +package org.bxteam.divinemc.dfc.common.ducks; + +public interface IBlendingAwareVisitor { + boolean c2me$isBlendingEnabled(); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/ICoordinatesFilling.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/ICoordinatesFilling.java new file mode 100644 index 0000000..4fa5595 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/ICoordinatesFilling.java @@ -0,0 +1,5 @@ +package org.bxteam.divinemc.dfc.common.ducks; + +public interface ICoordinatesFilling { + void c2me$fillCoordinates(int[] var1, int[] var2, int[] var3); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IEqualityOverriding.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IEqualityOverriding.java new file mode 100644 index 0000000..9236883 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IEqualityOverriding.java @@ -0,0 +1,7 @@ +package org.bxteam.divinemc.dfc.common.ducks; + +public interface IEqualityOverriding { + void c2me$overrideEquality(Object var1); + + Object c2me$getOverriddenEquality(); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IFastCacheLike.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IFastCacheLike.java new file mode 100644 index 0000000..8289c85 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/ducks/IFastCacheLike.java @@ -0,0 +1,20 @@ +package org.bxteam.divinemc.dfc.common.ducks; + +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import net.minecraft.world.level.levelgen.DensityFunction; + +public interface IFastCacheLike extends DensityFunction { + long CACHE_MISS_NAN_BITS = 9222769054270909007L; + + double c2me$getCached(int var1, int var2, int var3, EvalType var4); + + boolean c2me$getCached(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5); + + void c2me$cache(int var1, int var2, int var3, EvalType var4, double var5); + + void c2me$cache(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5); + + DensityFunction c2me$getDelegate(); + + DensityFunction c2me$withDelegate(DensityFunction var1); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/BytecodeGen.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/BytecodeGen.java new file mode 100644 index 0000000..365ed1a --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/BytecodeGen.java @@ -0,0 +1,499 @@ +package org.bxteam.divinemc.dfc.common.gen; + +import com.google.common.io.Files; +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.ast.McToAst; +import org.bxteam.divinemc.dfc.common.ast.dfvisitor.StripBlending; +import org.bxteam.divinemc.dfc.common.ast.misc.ConstantNode; +import org.bxteam.divinemc.dfc.common.ast.misc.RootNode; +import org.bxteam.divinemc.dfc.common.util.ArrayCache; +import org.bxteam.divinemc.dfc.common.vif.AstVanillaInterface; +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.IntObjectPair; +import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceMaps; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.IntConsumer; +import java.util.stream.Collectors; +import net.minecraft.util.CubicSpline; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.DensityFunctions; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.AnalyzerAdapter; +import org.objectweb.asm.commons.InstructionAdapter; + +public class BytecodeGen { + private static final File exportDir = new File("./cache/c2me-dfc"); + private static final AtomicLong ordinal = new AtomicLong(); + public static final Hash.Strategy RELAXED_STRATEGY; + private static final Object2ReferenceMap> compilationCache; + + public BytecodeGen() { + } + + public static DensityFunction compile(DensityFunction densityFunction, Reference2ReferenceMap tempCache) { + DensityFunction cached = (DensityFunction)tempCache.get(densityFunction); + if (cached != null) { + return cached; + } else if (densityFunction instanceof AstVanillaInterface) { + AstVanillaInterface vif = (AstVanillaInterface)densityFunction; + AstNode ast = vif.getAstNode(); + return new CompiledDensityFunction(compile0(ast), vif.getBlendingFallback()); + } else { + AstNode ast = McToAst.toAst(densityFunction.mapAll(StripBlending.INSTANCE)); + if (ast instanceof ConstantNode) { + ConstantNode constantNode = (ConstantNode)ast; + return DensityFunctions.constant(constantNode.getValue()); + } else { + CompiledDensityFunction compiled = new CompiledDensityFunction(compile0(ast), densityFunction); + tempCache.put(densityFunction, compiled); + return compiled; + } + } + } + + public static synchronized CompiledEntry compile0(AstNode node) { + Class cached = (Class)compilationCache.get(node); + ClassWriter writer = new ClassWriter(3); + String name = cached != null ? String.format("DfcCompiled_discarded") : String.format("DfcCompiled_%d", ordinal.getAndIncrement()); + writer.visit(65, 17, name, (String)null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(CompiledEntry.class)}); + RootNode rootNode = new RootNode(node); + Context genContext = new Context(writer, name); + genContext.newSingleMethod0((adapter, localVarConsumer) -> { + rootNode.doBytecodeGenSingle(genContext, adapter, localVarConsumer); + }, "evalSingle", true); + genContext.newMultiMethod0((adapter, localVarConsumer) -> { + rootNode.doBytecodeGenMulti(genContext, adapter, localVarConsumer); + }, "evalMulti", true); + List args = (List)genContext.args.entrySet().stream().sorted(Comparator.comparingInt((o) -> { + return ((Context.FieldRecord)o.getValue()).ordinal(); + })).map(Map.Entry::getKey).collect(Collectors.toCollection(ArrayList::new)); + if (cached != null) { + try { + return (CompiledEntry)cached.getConstructor(List.class).newInstance(args); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException var11) { + ReflectiveOperationException e = var11; + throw new RuntimeException(e); + } + } else { + genConstructor(genContext); + genGetArgs(genContext); + genNewInstance(genContext); + + Object var8; + for(ListIterator iterator = args.listIterator(); iterator.hasNext(); var8 = iterator.next()) { + } + + byte[] bytes = writer.toByteArray(); + dumpClass(genContext.className, bytes); + Class defined = defineClass(genContext.className, bytes); + compilationCache.put(node, defined); + + try { + return (CompiledEntry)defined.getConstructor(List.class).newInstance(args); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException var12) { + ReflectiveOperationException e = var12; + throw new RuntimeException(e); + } + } + } + + private static void genConstructor(Context context) { + InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 1, "", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(List.class)}), context.classWriter.visitMethod(1, "", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(List.class)}), (String)null, (String[])null))); + Label start = new Label(); + Label end = new Label(); + m.visitLabel(start); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.invokespecial(Type.getInternalName(Object.class), "", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[0]), false); + Iterator var4 = context.args.entrySet().stream().sorted(Comparator.comparingInt((o) -> { + return ((Context.FieldRecord)o.getValue()).ordinal(); + })).toList().iterator(); + + while(var4.hasNext()) { + Map.Entry entry = (Map.Entry)var4.next(); + String name = ((Context.FieldRecord)entry.getValue()).name(); + Class type = ((Context.FieldRecord)entry.getValue()).type(); + int ordinal = ((Context.FieldRecord)entry.getValue()).ordinal(); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.iconst(ordinal); + m.invokeinterface(Type.getInternalName(List.class), "get", Type.getMethodDescriptor(InstructionAdapter.OBJECT_TYPE, new Type[]{Type.INT_TYPE})); + m.checkcast(Type.getType(type)); + m.putfield(context.className, name, Type.getDescriptor(type)); + } + + var4 = context.postProcessMethods.stream().sorted().toList().iterator(); + + while(var4.hasNext()) { + String postProcessingMethod = (String)var4.next(); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(context.className, postProcessingMethod, "()V", false); + } + + m.areturn(Type.VOID_TYPE); + m.visitLabel(end); + m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0); + m.visitLocalVariable("list", Type.getDescriptor(List.class), (String)null, start, end, 1); + m.visitMaxs(0, 0); + } + + private static void genGetArgs(Context context) { + InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 17, "getArgs", Type.getMethodDescriptor(Type.getType(List.class), new Type[0]), context.classWriter.visitMethod(17, "getArgs", Type.getMethodDescriptor(Type.getType(List.class), new Type[0]), (String)null, (String[])null))); + Label start = new Label(); + Label end = new Label(); + m.visitLabel(start); + m.anew(Type.getType(ArrayList.class)); + m.dup(); + m.iconst(context.args.size()); + m.invokespecial(Type.getInternalName(ArrayList.class), "", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.INT_TYPE}), false); + m.store(1, InstructionAdapter.OBJECT_TYPE); + Iterator var4 = context.args.entrySet().stream().sorted(Comparator.comparingInt((o) -> { + return ((Context.FieldRecord)o.getValue()).ordinal(); + })).toList().iterator(); + + while(var4.hasNext()) { + Map.Entry entry = (Map.Entry)var4.next(); + String name = ((Context.FieldRecord)entry.getValue()).name(); + Class type = ((Context.FieldRecord)entry.getValue()).type(); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.getfield(context.className, name, Type.getDescriptor(type)); + m.invokeinterface(Type.getInternalName(List.class), "add", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[]{InstructionAdapter.OBJECT_TYPE})); + m.pop(); + } + + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.areturn(InstructionAdapter.OBJECT_TYPE); + m.visitLabel(end); + m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0); + m.visitLocalVariable("list", Type.getDescriptor(List.class), (String)null, start, end, 1); + m.visitMaxs(0, 0); + } + + private static void genNewInstance(Context context) { + InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 17, "newInstance", Type.getMethodDescriptor(Type.getType(CompiledEntry.class), new Type[]{Type.getType(List.class)}), context.classWriter.visitMethod(17, "newInstance", Type.getMethodDescriptor(Type.getType(CompiledEntry.class), new Type[]{Type.getType(List.class)}), (String)null, (String[])null))); + Label start = new Label(); + Label end = new Label(); + m.visitLabel(start); + m.anew(Type.getType(context.classDesc)); + m.dup(); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.invokespecial(context.className, "", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(List.class)}), false); + m.areturn(InstructionAdapter.OBJECT_TYPE); + m.visitLabel(end); + m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0); + m.visitLocalVariable("list", Type.getDescriptor(List.class), (String)null, start, end, 1); + m.visitMaxs(0, 0); + } + + private static void dumpClass(String className, byte[] bytes) { + File outputFile = new File(exportDir, className + ".class"); + outputFile.getParentFile().mkdirs(); + + try { + Files.write(bytes, outputFile); + } catch (IOException var4) { + IOException e = var4; + e.printStackTrace(); + } + + } + + private static Class defineClass(final String className, final byte[] bytes) { + ClassLoader classLoader = new ClassLoader(BytecodeGen.class.getClassLoader()) { + public Class loadClass(String name) throws ClassNotFoundException { + return name.equals(className) ? super.defineClass(name, bytes, 0, bytes.length) : super.loadClass(name); + } + }; + + try { + return classLoader.loadClass(className); + } catch (ClassNotFoundException var4) { + ClassNotFoundException e = var4; + throw new RuntimeException(e); + } + } + + static { + try { + org.bxteam.divinemc.util.Files.deleteRecursively(exportDir); + } catch (IOException var1) { + IOException e = var1; + e.printStackTrace(); + } + + RELAXED_STRATEGY = new Hash.Strategy() { + public int hashCode(AstNode o) { + return o.relaxedHashCode(); + } + + public boolean equals(AstNode a, AstNode b) { + return a.relaxedEquals(b); + } + }; + compilationCache = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenCustomHashMap<>(RELAXED_STRATEGY)); + } + + public static class Context { + public static final String SINGLE_DESC; + public static final String MULTI_DESC; + public final ClassWriter classWriter; + public final String className; + public final String classDesc; + private int methodIdx = 0; + private final Object2ReferenceOpenHashMap singleMethods = new Object2ReferenceOpenHashMap<>(); + private final Object2ReferenceOpenHashMap multiMethods = new Object2ReferenceOpenHashMap<>(); + private final Object2ReferenceOpenHashMap, String> splineMethods = new Object2ReferenceOpenHashMap<>(); + private final ObjectOpenHashSet postProcessMethods = new ObjectOpenHashSet<>(); + private final Reference2ObjectOpenHashMap args = new Reference2ObjectOpenHashMap<>(); + + public Context(ClassWriter classWriter, String className) { + this.classWriter = (ClassWriter)Objects.requireNonNull(classWriter); + this.className = (String)Objects.requireNonNull(className); + this.classDesc = String.format("L%s;", this.className); + } + + public String nextMethodName() { + return String.format("method_%d", this.methodIdx++); + } + + public String nextMethodName(String suffix) { + return String.format("method_%d_%s", this.methodIdx++, suffix); + } + + public String newSingleMethod(AstNode node) { + return this.singleMethods.computeIfAbsent(node, (AstNode node1) -> this.newSingleMethod((adapter, localVarConsumer) -> node1.doBytecodeGenSingle(this, adapter, localVarConsumer), nextMethodName(node.getClass().getSimpleName()))); + } + + public String newSingleMethod(BiConsumer generator) { + return this.newSingleMethod(generator, this.nextMethodName()); + } + + public String newSingleMethod(BiConsumer generator, String name) { + this.newSingleMethod0(generator, name, false); + return name; + } + + private void newSingleMethod0(BiConsumer generator, String name, boolean isPublic) { + InstructionAdapter adapter = new InstructionAdapter(new AnalyzerAdapter(this.className, (isPublic ? 1 : 2) | 16, name, SINGLE_DESC, this.classWriter.visitMethod((isPublic ? 1 : 2) | 16, name, SINGLE_DESC, (String)null, (String[])null))); + List>> extraLocals = new ArrayList<>(); + Label start = new Label(); + Label end = new Label(); + adapter.visitLabel(start); + generator.accept(adapter, (localName, localDesc) -> { + int ordinal = extraLocals.size() + 5; + extraLocals.add(IntObjectPair.of(ordinal, Pair.of(localName, localDesc))); + return ordinal; + }); + adapter.visitLabel(end); + adapter.visitLocalVariable("this", this.classDesc, (String)null, start, end, 0); + adapter.visitLocalVariable("x", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 1); + adapter.visitLocalVariable("y", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 2); + adapter.visitLocalVariable("z", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 3); + adapter.visitLocalVariable("evalType", Type.getType(EvalType.class).getDescriptor(), (String)null, start, end, 4); + Iterator var8 = extraLocals.iterator(); + + while(var8.hasNext()) { + IntObjectPair> local = (IntObjectPair)var8.next(); + adapter.visitLocalVariable((String)((Pair)local.right()).left(), (String)((Pair)local.right()).right(), (String)null, start, end, local.leftInt()); + } + + adapter.visitMaxs(0, 0); + } + + public String newMultiMethod(AstNode node) { + return this.multiMethods.computeIfAbsent(node, (AstNode node1) -> this.newMultiMethod((adapter, localVarConsumer) -> node1.doBytecodeGenMulti(this, adapter, localVarConsumer), nextMethodName(node.getClass().getSimpleName()))); + } + + public String newMultiMethod(BiConsumer generator) { + return this.newMultiMethod(generator, this.nextMethodName()); + } + + public String newMultiMethod(BiConsumer generator, String name) { + this.newMultiMethod0(generator, name, false); + return name; + } + + private void newMultiMethod0(BiConsumer generator, String name, boolean isPublic) { + InstructionAdapter adapter = new InstructionAdapter(new AnalyzerAdapter(this.className, (isPublic ? 1 : 2) | 16, name, MULTI_DESC, this.classWriter.visitMethod((isPublic ? 1 : 2) | 16, name, MULTI_DESC, (String)null, (String[])null))); + List>> extraLocals = new ArrayList(); + Label start = new Label(); + Label end = new Label(); + adapter.visitLabel(start); + generator.accept(adapter, (localName, localDesc) -> { + int ordinal = extraLocals.size() + 7; + extraLocals.add(IntObjectPair.of(ordinal, Pair.of(localName, localDesc))); + return ordinal; + }); + adapter.visitLabel(end); + adapter.visitLocalVariable("this", this.classDesc, (String)null, start, end, 0); + adapter.visitLocalVariable("res", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 1); + adapter.visitLocalVariable("x", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 2); + adapter.visitLocalVariable("y", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 3); + adapter.visitLocalVariable("z", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 4); + adapter.visitLocalVariable("evalType", Type.getType(EvalType.class).getDescriptor(), (String)null, start, end, 5); + adapter.visitLocalVariable("arrayCache", Type.getType(ArrayCache.class).getDescriptor(), (String)null, start, end, 6); + Iterator var8 = extraLocals.iterator(); + + while(var8.hasNext()) { + IntObjectPair> local = (IntObjectPair)var8.next(); + adapter.visitLocalVariable((String)((Pair)local.right()).left(), (String)((Pair)local.right()).right(), (String)null, start, end, local.leftInt()); + } + + adapter.visitMaxs(0, 0); + } + + public String getCachedSplineMethod(CubicSpline spline) { + return (String)this.splineMethods.get(spline); + } + + public void cacheSplineMethod(CubicSpline spline, String method) { + this.splineMethods.put(spline, method); + } + + public void callDelegateSingle(InstructionAdapter m, String target) { + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(1, Type.INT_TYPE); + m.load(2, Type.INT_TYPE); + m.load(3, Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(this.className, target, SINGLE_DESC, false); + } + + public void callDelegateMulti(InstructionAdapter m, String target) { + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.load(6, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(this.className, target, MULTI_DESC, false); + } + + public String newField(Class type, T data) { + FieldRecord existing = (FieldRecord)this.args.get(data); + if (existing != null) { + return existing.name(); + } else { + int size = this.args.size(); + String name = String.format("field_%d", size); + this.classWriter.visitField(2, name, Type.getDescriptor(type), (String)null, (Object)null); + this.args.put(data, new FieldRecord(name, size, type)); + return name; + } + } + + public void doCountedLoop(InstructionAdapter m, LocalVarConsumer localVarConsumer, IntConsumer bodyGenerator) { + int loopIdx = localVarConsumer.createLocalVariable("loopIdx", Type.INT_TYPE.getDescriptor()); + m.iconst(0); + m.store(loopIdx, Type.INT_TYPE); + Label start = new Label(); + Label end = new Label(); + m.visitLabel(start); + m.load(loopIdx, Type.INT_TYPE); + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.arraylength(); + m.ificmpge(end); + bodyGenerator.accept(loopIdx); + m.iinc(loopIdx, 1); + m.goTo(start); + m.visitLabel(end); + } + + public void delegateToSingle(InstructionAdapter m, LocalVarConsumer localVarConsumer, AstNode current) { + String singleMethod = this.newSingleMethod(current); + this.doCountedLoop(m, localVarConsumer, (idx) -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + m.invokevirtual(this.className, singleMethod, SINGLE_DESC, false); + m.astore(Type.DOUBLE_TYPE); + }); + } + + public void genPostprocessingMethod(String name, Consumer generator) { + if (!this.postProcessMethods.contains(name)) { + InstructionAdapter adapter = new InstructionAdapter(new AnalyzerAdapter(this.className, 18, name, "()V", this.classWriter.visitMethod(18, name, "()V", (String)null, (String[])null))); + Label start = new Label(); + Label end = new Label(); + adapter.visitLabel(start); + generator.accept(adapter); + adapter.visitLabel(end); + adapter.visitMaxs(0, 0); + adapter.visitLocalVariable("this", this.classDesc, (String)null, start, end, 0); + this.postProcessMethods.add(name); + } + } + + static { + SINGLE_DESC = Type.getMethodDescriptor(Type.getType(Double.TYPE), new Type[]{Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(EvalType.class)}); + MULTI_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class), Type.getType(ArrayCache.class)}); + } + + public interface LocalVarConsumer { + int createLocalVariable(String var1, String var2); + } + + private static record FieldRecord(String name, int ordinal, Class type) { + private FieldRecord(String name, int ordinal, Class type) { + this.name = name; + this.ordinal = ordinal; + this.type = type; + } + + public String name() { + return this.name; + } + + public int ordinal() { + return this.ordinal; + } + + public Class type() { + return this.type; + } + } + } + + @FunctionalInterface + public interface EvalMultiInterface { + void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5); + } + + @FunctionalInterface + public interface EvalSingleInterface { + double evalSingle(int var1, int var2, int var3, EvalType var4); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/CompiledDensityFunction.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/CompiledDensityFunction.java new file mode 100644 index 0000000..15b6c16 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/CompiledDensityFunction.java @@ -0,0 +1,98 @@ +package org.bxteam.divinemc.dfc.common.gen; + +import com.google.common.base.Suppliers; +import org.bxteam.divinemc.dfc.common.ducks.IBlendingAwareVisitor; +import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike; +import java.util.List; +import java.util.ListIterator; +import java.util.Objects; +import java.util.function.Supplier; +import net.minecraft.world.level.levelgen.DensityFunction; + +public class CompiledDensityFunction extends SubCompiledDensityFunction { + private final CompiledEntry compiledEntry; + + public CompiledDensityFunction(CompiledEntry compiledEntry, DensityFunction blendingFallback) { + super(compiledEntry, compiledEntry, blendingFallback); + this.compiledEntry = (CompiledEntry)Objects.requireNonNull(compiledEntry); + } + + private CompiledDensityFunction(CompiledEntry compiledEntry, Supplier blendingFallback) { + super(compiledEntry, compiledEntry, blendingFallback); + this.compiledEntry = (CompiledEntry)Objects.requireNonNull(compiledEntry); + } + + public DensityFunction mapAll(Visitor visitor) { + if (visitor instanceof IBlendingAwareVisitor blendingAwareVisitor) { + if (blendingAwareVisitor.c2me$isBlendingEnabled()) { + DensityFunction fallback1 = this.getFallback(); + if (fallback1 == null) { + throw new IllegalStateException("blendingFallback is no more"); + } + + return fallback1.mapAll(visitor); + } + } + + boolean modified = false; + List args = this.compiledEntry.getArgs(); + ListIterator iterator = args.listIterator(); + + Object next; + while(iterator.hasNext()) { + next = iterator.next(); + if (next instanceof DensityFunction df) { + if (!(df instanceof IFastCacheLike)) { + DensityFunction applied = df.mapAll(visitor); + if (df != applied) { + iterator.set(applied); + modified = true; + } + } + } + + if (next instanceof NoiseHolder noise) { + NoiseHolder applied = visitor.visitNoise(noise); + if (noise != applied) { + iterator.set(applied); + modified = true; + } + } + } + + iterator = args.listIterator(); + + while(iterator.hasNext()) { + next = iterator.next(); + if (next instanceof IFastCacheLike cacheLike) { + DensityFunction applied = visitor.apply(cacheLike); + if (applied == cacheLike.c2me$getDelegate()) { + iterator.set((Object)null); + modified = true; + } else { + if (!(applied instanceof IFastCacheLike)) { + throw new UnsupportedOperationException("Unsupported transformation on Wrapping node"); + } + + IFastCacheLike newCacheLike = (IFastCacheLike)applied; + iterator.set(newCacheLike); + modified = true; + } + } + } + + Supplier fallback = this.blendingFallback != null ? Suppliers.memoize(() -> { + DensityFunction densityFunction = (DensityFunction)this.blendingFallback.get(); + return densityFunction != null ? densityFunction.mapAll(visitor) : null; + }) : null; + if (fallback != this.blendingFallback) { + modified = true; + } + + if (modified) { + return new CompiledDensityFunction(this.compiledEntry.newInstance(args), fallback); + } else { + return this; + } + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/CompiledEntry.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/CompiledEntry.java new file mode 100644 index 0000000..35b0867 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/CompiledEntry.java @@ -0,0 +1,15 @@ +package org.bxteam.divinemc.dfc.common.gen; + +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.util.ArrayCache; +import java.util.List; + +public interface CompiledEntry extends ISingleMethod, IMultiMethod { + double evalSingle(int var1, int var2, int var3, EvalType var4); + + void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5, ArrayCache var6); + + CompiledEntry newInstance(List var1); + + List getArgs(); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/DelegatingBlendingAwareVisitor.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/DelegatingBlendingAwareVisitor.java new file mode 100644 index 0000000..1b4ec4e --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/DelegatingBlendingAwareVisitor.java @@ -0,0 +1,28 @@ +package org.bxteam.divinemc.dfc.common.gen; + +import org.bxteam.divinemc.dfc.common.ducks.IBlendingAwareVisitor; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; +import org.jetbrains.annotations.NotNull; + +public class DelegatingBlendingAwareVisitor implements IBlendingAwareVisitor, DensityFunction.Visitor { + private final DensityFunction.Visitor delegate; + private final boolean blendingEnabled; + + public DelegatingBlendingAwareVisitor(DensityFunction.Visitor delegate, boolean blendingEnabled) { + this.delegate = Objects.requireNonNull(delegate); + this.blendingEnabled = blendingEnabled; + } + + public @NotNull DensityFunction apply(@NotNull DensityFunction densityFunction) { + return this.delegate.apply(densityFunction); + } + + public DensityFunction.@NotNull NoiseHolder visitNoise(DensityFunction.@NotNull NoiseHolder noiseDensityFunction) { + return this.delegate.visitNoise(noiseDensityFunction); + } + + public boolean c2me$isBlendingEnabled() { + return this.blendingEnabled; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/IMultiMethod.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/IMultiMethod.java new file mode 100644 index 0000000..21b2796 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/IMultiMethod.java @@ -0,0 +1,9 @@ +package org.bxteam.divinemc.dfc.common.gen; + +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.util.ArrayCache; + +@FunctionalInterface +public interface IMultiMethod { + void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5, ArrayCache var6); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/ISingleMethod.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/ISingleMethod.java new file mode 100644 index 0000000..f946553 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/ISingleMethod.java @@ -0,0 +1,8 @@ +package org.bxteam.divinemc.dfc.common.gen; + +import org.bxteam.divinemc.dfc.common.ast.EvalType; + +@FunctionalInterface +public interface ISingleMethod { + double evalSingle(int var1, int var2, int var3, EvalType var4); +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/SubCompiledDensityFunction.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/SubCompiledDensityFunction.java new file mode 100644 index 0000000..aec0c62 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/gen/SubCompiledDensityFunction.java @@ -0,0 +1,142 @@ +package org.bxteam.divinemc.dfc.common.gen; + +import com.google.common.base.Suppliers; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.ducks.IArrayCacheCapable; +import org.bxteam.divinemc.dfc.common.ducks.IBlendingAwareVisitor; +import org.bxteam.divinemc.dfc.common.ducks.ICoordinatesFilling; +import org.bxteam.divinemc.dfc.common.util.ArrayCache; +import org.bxteam.divinemc.dfc.common.vif.EachApplierVanillaInterface; +import java.util.Objects; +import java.util.function.Supplier; +import net.minecraft.util.KeyDispatchDataCodec; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.NoiseChunk; +import net.minecraft.world.level.levelgen.blending.Blender; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SubCompiledDensityFunction implements DensityFunction { + private static final Logger LOGGER = LoggerFactory.getLogger(SubCompiledDensityFunction.class); + private final ISingleMethod singleMethod; + private final IMultiMethod multiMethod; + protected final Supplier blendingFallback; + + public SubCompiledDensityFunction(ISingleMethod singleMethod, IMultiMethod multiMethod, DensityFunction blendingFallback) { + this(singleMethod, multiMethod, unwrap(blendingFallback)); + } + + protected SubCompiledDensityFunction(ISingleMethod singleMethod, IMultiMethod multiMethod, Supplier blendingFallback) { + this.singleMethod = (ISingleMethod)Objects.requireNonNull(singleMethod); + this.multiMethod = (IMultiMethod)Objects.requireNonNull(multiMethod); + this.blendingFallback = blendingFallback; + } + + private static Supplier unwrap(DensityFunction densityFunction) { + if (densityFunction instanceof SubCompiledDensityFunction scdf) { + return scdf.blendingFallback; + } else { + return densityFunction != null ? Suppliers.ofInstance(densityFunction) : null; + } + } + + public double compute(FunctionContext pos) { + if (pos.getBlender() != Blender.empty()) { + DensityFunction fallback = this.getFallback(); + if (fallback == null) { + throw new IllegalStateException("blendingFallback is no more"); + } else { + return fallback.compute(pos); + } + } else { + return this.singleMethod.evalSingle(pos.blockX(), pos.blockY(), pos.blockZ(), EvalType.from(pos)); + } + } + + public void fillArray(double[] densities, ContextProvider applier) { + if (applier instanceof NoiseChunk sampler) { + if (sampler.getBlender() != Blender.empty()) { + DensityFunction fallback = this.getFallback(); + if (fallback == null) { + throw new IllegalStateException("blendingFallback is no more"); + } + + fallback.fillArray(densities, applier); + return; + } + } + + if (applier instanceof EachApplierVanillaInterface vanillaInterface) { + this.multiMethod.evalMulti(densities, vanillaInterface.getX(), vanillaInterface.getY(), vanillaInterface.getZ(), EvalType.from(applier), vanillaInterface.c2me$getArrayCache()); + } else { + ArrayCache var10000; + if (applier instanceof IArrayCacheCapable cacheCapable) { + var10000 = cacheCapable.c2me$getArrayCache(); + } else { + var10000 = new ArrayCache(); + } + + ArrayCache cache = var10000; + int[] x = cache.getIntArray(densities.length, false); + int[] y = cache.getIntArray(densities.length, false); + int[] z = cache.getIntArray(densities.length, false); + if (applier instanceof ICoordinatesFilling coordinatesFilling) { + coordinatesFilling.c2me$fillCoordinates(x, y, z); + } else { + for(int i = 0; i < densities.length; ++i) { + FunctionContext pos = applier.forIndex(i); + x[i] = pos.blockX(); + y[i] = pos.blockY(); + z[i] = pos.blockZ(); + } + } + + this.multiMethod.evalMulti(densities, x, y, z, EvalType.from(applier), cache); + } + } + + public DensityFunction mapAll(Visitor visitor) { + if (this.getClass() != SubCompiledDensityFunction.class) { + throw new AbstractMethodError(); + } else { + if (visitor instanceof IBlendingAwareVisitor) { + IBlendingAwareVisitor blendingAwareVisitor = (IBlendingAwareVisitor)visitor; + if (blendingAwareVisitor.c2me$isBlendingEnabled()) { + DensityFunction fallback1 = this.getFallback(); + if (fallback1 == null) { + throw new IllegalStateException("blendingFallback is no more"); + } + + return fallback1.mapAll(visitor); + } + } + + boolean modified = false; + Supplier fallback = this.blendingFallback != null ? Suppliers.memoize(() -> { + DensityFunction densityFunction = (DensityFunction)this.blendingFallback.get(); + return densityFunction != null ? densityFunction.mapAll(visitor) : null; + }) : null; + if (fallback != this.blendingFallback) { + modified = true; + } + + return modified ? new SubCompiledDensityFunction(this.singleMethod, this.multiMethod, fallback) : this; + } + } + + public double minValue() { + return Double.MIN_VALUE; + } + + public double maxValue() { + return Double.MAX_VALUE; + } + + public KeyDispatchDataCodec codec() { + throw new UnsupportedOperationException(); + } + + protected DensityFunction getFallback() { + return this.blendingFallback != null ? (DensityFunction)this.blendingFallback.get() : null; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/util/ArrayCache.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/util/ArrayCache.java new file mode 100644 index 0000000..a3813ca --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/util/ArrayCache.java @@ -0,0 +1,57 @@ +package org.bxteam.divinemc.dfc.common.util; + +import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import java.util.Arrays; + +public class ArrayCache { + private final Int2ReferenceArrayMap> doubleArrayCache = new Int2ReferenceArrayMap<>(); + private final Int2ReferenceArrayMap> intArrayCache = new Int2ReferenceArrayMap<>(); + + public ArrayCache() { + } + + public double[] getDoubleArray(int size, boolean zero) { + ReferenceArrayList list = this.doubleArrayCache.computeIfAbsent(size, (k) -> { + return new ReferenceArrayList<>(); + }); + if (list.isEmpty()) { + return new double[size]; + } else { + double[] popped = list.pop(); + if (zero) { + Arrays.fill(popped, 0.0); + } + + return popped; + } + } + + public int[] getIntArray(int size, boolean zero) { + ReferenceArrayList list = this.intArrayCache.computeIfAbsent(size, (k) -> { + return new ReferenceArrayList<>(); + }); + if (list.isEmpty()) { + return new int[size]; + } else { + int[] popped = list.pop(); + if (zero) { + Arrays.fill(popped, 0); + } + + return popped; + } + } + + public void recycle(double[] array) { + this.doubleArrayCache.computeIfAbsent(array.length, (k) -> { + return new ReferenceArrayList<>(); + }).add(array); + } + + public void recycle(int[] array) { + this.intArrayCache.computeIfAbsent(array.length, (k) -> { + return new ReferenceArrayList<>(); + }).add(array); + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/vif/AstVanillaInterface.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/vif/AstVanillaInterface.java new file mode 100644 index 0000000..5b8c9ef --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/vif/AstVanillaInterface.java @@ -0,0 +1,98 @@ +package org.bxteam.divinemc.dfc.common.vif; + +import org.bxteam.divinemc.dfc.common.ast.AstNode; +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.ast.misc.CacheLikeNode; +import org.bxteam.divinemc.dfc.common.ast.misc.DelegateNode; +import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike; +import java.util.Objects; +import net.minecraft.util.KeyDispatchDataCodec; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.NoiseChunk; +import net.minecraft.world.level.levelgen.blending.Blender; + +public class AstVanillaInterface implements DensityFunction { + private final AstNode astNode; + private final DensityFunction blendingFallback; + + public AstVanillaInterface(AstNode astNode, DensityFunction blendingFallback) { + this.astNode = (AstNode)Objects.requireNonNull(astNode); + this.blendingFallback = blendingFallback; + } + + public double compute(FunctionContext pos) { + if (pos.getBlender() != Blender.empty()) { + if (this.blendingFallback == null) { + throw new IllegalStateException("blendingFallback is no more"); + } else { + return this.blendingFallback.compute(pos); + } + } else { + return this.astNode.evalSingle(pos.blockX(), pos.blockY(), pos.blockZ(), EvalType.from(pos)); + } + } + + public void fillArray(double[] densities, ContextProvider applier) { + if (applier instanceof NoiseChunk sampler) { + if (sampler.getBlender() != Blender.empty()) { + if (this.blendingFallback == null) { + throw new IllegalStateException("blendingFallback is no more"); + } + + this.blendingFallback.fillArray(densities, applier); + return; + } + } + + if (applier instanceof EachApplierVanillaInterface vanillaInterface) { + this.astNode.evalMulti(densities, vanillaInterface.getX(), vanillaInterface.getY(), vanillaInterface.getZ(), EvalType.from(applier)); + } else { + int[] x = new int[densities.length]; + int[] y = new int[densities.length]; + int[] z = new int[densities.length]; + + for(int i = 0; i < densities.length; ++i) { + FunctionContext pos = applier.forIndex(i); + x[i] = pos.blockX(); + y[i] = pos.blockY(); + z[i] = pos.blockZ(); + } + + this.astNode.evalMulti(densities, x, y, z, EvalType.from(applier)); + } + } + + public DensityFunction mapAll(Visitor visitor) { + AstNode transformed = this.astNode.transform((astNode) -> { + if (astNode instanceof DelegateNode delegateNode) { + return new DelegateNode(delegateNode.getDelegate().mapAll(visitor)); + } else if (astNode instanceof CacheLikeNode cacheLikeNode) { + return new CacheLikeNode((IFastCacheLike)cacheLikeNode.getCacheLike().mapAll(visitor), cacheLikeNode.getDelegate()); + } else { + return astNode; + } + }); + DensityFunction blendingFallback1 = this.blendingFallback != null ? this.blendingFallback.mapAll(visitor) : null; + return transformed == this.astNode && blendingFallback1 == this.blendingFallback ? this : new AstVanillaInterface(transformed, blendingFallback1); + } + + public double minValue() { + return this.blendingFallback.minValue(); + } + + public double maxValue() { + return this.blendingFallback.maxValue(); + } + + public KeyDispatchDataCodec codec() { + throw new UnsupportedOperationException(); + } + + public AstNode getAstNode() { + return this.astNode; + } + + public DensityFunction getBlendingFallback() { + return this.blendingFallback; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/vif/EachApplierVanillaInterface.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/vif/EachApplierVanillaInterface.java new file mode 100644 index 0000000..d557a82 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/vif/EachApplierVanillaInterface.java @@ -0,0 +1,58 @@ +package org.bxteam.divinemc.dfc.common.vif; + +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import org.bxteam.divinemc.dfc.common.ducks.IArrayCacheCapable; +import org.bxteam.divinemc.dfc.common.util.ArrayCache; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; + +public class EachApplierVanillaInterface implements DensityFunction.ContextProvider, IArrayCacheCapable { + private final int[] x; + private final int[] y; + private final int[] z; + private final EvalType type; + private final ArrayCache cache; + + public EachApplierVanillaInterface(int[] x, int[] y, int[] z, EvalType type) { + this(x, y, z, type, new ArrayCache()); + } + + public EachApplierVanillaInterface(int[] x, int[] y, int[] z, EvalType type, ArrayCache cache) { + this.x = (int[])Objects.requireNonNull(x); + this.y = (int[])Objects.requireNonNull(y); + this.z = (int[])Objects.requireNonNull(z); + this.type = (EvalType)Objects.requireNonNull(type); + this.cache = (ArrayCache)Objects.requireNonNull(cache); + } + + public DensityFunction.FunctionContext forIndex(int index) { + return new NoisePosVanillaInterface(this.x[index], this.y[index], this.z[index], this.type); + } + + public void fillAllDirectly(double[] densities, DensityFunction densityFunction) { + for(int i = 0; i < this.x.length; ++i) { + densities[i] = densityFunction.compute(this.forIndex(i)); + } + + } + + public int[] getX() { + return this.x; + } + + public int[] getY() { + return this.y; + } + + public int[] getZ() { + return this.z; + } + + public EvalType getType() { + return this.type; + } + + public ArrayCache c2me$getArrayCache() { + return this.cache; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/vif/NoisePosVanillaInterface.java b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/vif/NoisePosVanillaInterface.java new file mode 100644 index 0000000..88b83cc --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/dfc/common/vif/NoisePosVanillaInterface.java @@ -0,0 +1,35 @@ +package org.bxteam.divinemc.dfc.common.vif; + +import org.bxteam.divinemc.dfc.common.ast.EvalType; +import java.util.Objects; +import net.minecraft.world.level.levelgen.DensityFunction; + +public class NoisePosVanillaInterface implements DensityFunction.FunctionContext { + private final int x; + private final int y; + private final int z; + private final EvalType type; + + public NoisePosVanillaInterface(int x, int y, int z, EvalType type) { + this.x = x; + this.y = y; + this.z = z; + this.type = (EvalType)Objects.requireNonNull(type); + } + + public int blockX() { + return this.x; + } + + public int blockY() { + return this.y; + } + + public int blockZ() { + return this.z; + } + + public EvalType getType() { + return this.type; + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/util/Assertions.java b/divinemc-server/src/main/java/org/bxteam/divinemc/util/Assertions.java new file mode 100644 index 0000000..98975c5 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/util/Assertions.java @@ -0,0 +1,27 @@ +package org.bxteam.divinemc.util; + +public final class Assertions { + public static void assertTrue(boolean value, String message) { + if (!value) { + final AssertionError error = new AssertionError(message); + error.printStackTrace(); + throw error; + } + } + + public static void assertTrue(boolean state, String format, Object... args) { + if (!state) { + final AssertionError error = new AssertionError(String.format(format, args)); + error.printStackTrace(); + throw error; + } + } + + public static void assertTrue(boolean value) { + if (!value) { + final AssertionError error = new AssertionError(); + error.printStackTrace(); + throw error; + } + } +} diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/util/Files.java b/divinemc-server/src/main/java/org/bxteam/divinemc/util/Files.java new file mode 100644 index 0000000..7d02eb2 --- /dev/null +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/util/Files.java @@ -0,0 +1,35 @@ +package org.bxteam.divinemc.util; + +import java.io.File; +import java.io.IOException; + +public final class Files { + public static void deleteRecursively(File dir) throws IOException { + if (dir == null || !dir.isDirectory()) { + return; + } + + try { + File[] files = dir.listFiles(); + if (files == null) { + throw new IOException("Error enumerating directory during recursive delete operation: " + dir.getAbsolutePath()); + } + + for (File child : files) { + if (child.isDirectory()) { + Files.deleteRecursively(child); + } else if (child.isFile()) { + if (!child.delete()) { + throw new IOException("Error deleting file during recursive delete operation: " + child.getAbsolutePath()); + } + } + } + + if (!dir.delete()) { + throw new IOException("Error deleting directory during recursive delete operation: " + dir.getAbsolutePath()); + } + } catch (SecurityException ex) { + throw new IOException("Security error during recursive delete operation", ex); + } + } +}