538 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			538 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.level.biome;
 | |
| 
 | |
| import com.google.common.annotations.VisibleForTesting;
 | |
| import com.google.common.collect.ImmutableList;
 | |
| import com.google.common.collect.Lists;
 | |
| import com.mojang.datafixers.util.Pair;
 | |
| import com.mojang.serialization.Codec;
 | |
| import com.mojang.serialization.DataResult;
 | |
| import com.mojang.serialization.MapCodec;
 | |
| import com.mojang.serialization.codecs.RecordCodecBuilder;
 | |
| import java.util.ArrayList;
 | |
| import java.util.Arrays;
 | |
| import java.util.Comparator;
 | |
| import java.util.Iterator;
 | |
| import java.util.List;
 | |
| import java.util.Locale;
 | |
| import java.util.stream.Collectors;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.QuartPos;
 | |
| import net.minecraft.util.ExtraCodecs;
 | |
| import net.minecraft.util.Mth;
 | |
| import net.minecraft.world.level.levelgen.DensityFunction;
 | |
| import net.minecraft.world.level.levelgen.DensityFunctions;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public class Climate {
 | |
| 	private static final boolean DEBUG_SLOW_BIOME_SEARCH = false;
 | |
| 	private static final float QUANTIZATION_FACTOR = 10000.0F;
 | |
| 	@VisibleForTesting
 | |
| 	protected static final int PARAMETER_COUNT = 7;
 | |
| 
 | |
| 	public static Climate.TargetPoint target(float temperature, float humidity, float continentalness, float erosion, float depth, float weirdness) {
 | |
| 		return new Climate.TargetPoint(
 | |
| 			quantizeCoord(temperature), quantizeCoord(humidity), quantizeCoord(continentalness), quantizeCoord(erosion), quantizeCoord(depth), quantizeCoord(weirdness)
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public static Climate.ParameterPoint parameters(
 | |
| 		float temperature, float humidity, float continentalness, float erosion, float depth, float weirdness, float offset
 | |
| 	) {
 | |
| 		return new Climate.ParameterPoint(
 | |
| 			Climate.Parameter.point(temperature),
 | |
| 			Climate.Parameter.point(humidity),
 | |
| 			Climate.Parameter.point(continentalness),
 | |
| 			Climate.Parameter.point(erosion),
 | |
| 			Climate.Parameter.point(depth),
 | |
| 			Climate.Parameter.point(weirdness),
 | |
| 			quantizeCoord(offset)
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public static Climate.ParameterPoint parameters(
 | |
| 		Climate.Parameter temperature,
 | |
| 		Climate.Parameter humidity,
 | |
| 		Climate.Parameter continentalness,
 | |
| 		Climate.Parameter erosion,
 | |
| 		Climate.Parameter depth,
 | |
| 		Climate.Parameter weirdness,
 | |
| 		float offset
 | |
| 	) {
 | |
| 		return new Climate.ParameterPoint(temperature, humidity, continentalness, erosion, depth, weirdness, quantizeCoord(offset));
 | |
| 	}
 | |
| 
 | |
| 	public static long quantizeCoord(float coord) {
 | |
| 		return (long)(coord * 10000.0F);
 | |
| 	}
 | |
| 
 | |
| 	public static float unquantizeCoord(long coord) {
 | |
| 		return (float)coord / 10000.0F;
 | |
| 	}
 | |
| 
 | |
| 	public static Climate.Sampler empty() {
 | |
| 		DensityFunction densityFunction = DensityFunctions.zero();
 | |
| 		return new Climate.Sampler(densityFunction, densityFunction, densityFunction, densityFunction, densityFunction, densityFunction, List.of());
 | |
| 	}
 | |
| 
 | |
| 	public static BlockPos findSpawnPosition(List<Climate.ParameterPoint> points, Climate.Sampler sampler) {
 | |
| 		return (new Climate.SpawnFinder(points, sampler)).result.location();
 | |
| 	}
 | |
| 
 | |
| 	interface DistanceMetric<T> {
 | |
| 		long distance(Climate.RTree.Node<T> node, long[] ls);
 | |
| 	}
 | |
| 
 | |
| 	public record Parameter(long min, long max) {
 | |
| 		public static final Codec<Climate.Parameter> CODEC = ExtraCodecs.intervalCodec(
 | |
| 			Codec.floatRange(-2.0F, 2.0F),
 | |
| 			"min",
 | |
| 			"max",
 | |
| 			(float_, float2) -> float_.compareTo(float2) > 0
 | |
| 				? DataResult.error(() -> "Cannon construct interval, min > max (" + float_ + " > " + float2 + ")")
 | |
| 				: DataResult.success(new Climate.Parameter(Climate.quantizeCoord(float_), Climate.quantizeCoord(float2))),
 | |
| 			parameter -> Climate.unquantizeCoord(parameter.min()),
 | |
| 			parameter -> Climate.unquantizeCoord(parameter.max())
 | |
| 		);
 | |
| 
 | |
| 		public static Climate.Parameter point(float value) {
 | |
| 			return span(value, value);
 | |
| 		}
 | |
| 
 | |
| 		public static Climate.Parameter span(float min, float max) {
 | |
| 			if (min > max) {
 | |
| 				throw new IllegalArgumentException("min > max: " + min + " " + max);
 | |
| 			} else {
 | |
| 				return new Climate.Parameter(Climate.quantizeCoord(min), Climate.quantizeCoord(max));
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public static Climate.Parameter span(Climate.Parameter min, Climate.Parameter max) {
 | |
| 			if (min.min() > max.max()) {
 | |
| 				throw new IllegalArgumentException("min > max: " + min + " " + max);
 | |
| 			} else {
 | |
| 				return new Climate.Parameter(min.min(), max.max());
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public String toString() {
 | |
| 			return this.min == this.max ? String.format(Locale.ROOT, "%d", this.min) : String.format(Locale.ROOT, "[%d-%d]", this.min, this.max);
 | |
| 		}
 | |
| 
 | |
| 		public long distance(long pointValue) {
 | |
| 			long l = pointValue - this.max;
 | |
| 			long m = this.min - pointValue;
 | |
| 			return l > 0L ? l : Math.max(m, 0L);
 | |
| 		}
 | |
| 
 | |
| 		public long distance(Climate.Parameter parameter) {
 | |
| 			long l = parameter.min() - this.max;
 | |
| 			long m = this.min - parameter.max();
 | |
| 			return l > 0L ? l : Math.max(m, 0L);
 | |
| 		}
 | |
| 
 | |
| 		public Climate.Parameter span(@Nullable Climate.Parameter param) {
 | |
| 			return param == null ? this : new Climate.Parameter(Math.min(this.min, param.min()), Math.max(this.max, param.max()));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static class ParameterList<T> {
 | |
| 		private final List<Pair<Climate.ParameterPoint, T>> values;
 | |
| 		private final Climate.RTree<T> index;
 | |
| 
 | |
| 		public static <T> Codec<Climate.ParameterList<T>> codec(MapCodec<T> codec) {
 | |
| 			return ExtraCodecs.nonEmptyList(
 | |
| 					RecordCodecBuilder.<T>create(
 | |
| 							instance -> instance.group(Climate.ParameterPoint.CODEC.fieldOf("parameters").forGetter(Pair::getFirst), codec.forGetter(Pair::getSecond))
 | |
| 								.apply(instance, Pair::of)
 | |
| 						)
 | |
| 						.listOf()
 | |
| 				)
 | |
| 				.xmap(Climate.ParameterList::new, Climate.ParameterList::values);
 | |
| 		}
 | |
| 
 | |
| 		public ParameterList(List<Pair<Climate.ParameterPoint, T>> values) {
 | |
| 			this.values = values;
 | |
| 			this.index = Climate.RTree.create(values);
 | |
| 		}
 | |
| 
 | |
| 		public List<Pair<Climate.ParameterPoint, T>> values() {
 | |
| 			return this.values;
 | |
| 		}
 | |
| 
 | |
| 		public T findValue(Climate.TargetPoint targetPoint) {
 | |
| 			return this.findValueIndex(targetPoint);
 | |
| 		}
 | |
| 
 | |
| 		@VisibleForTesting
 | |
| 		public T findValueBruteForce(Climate.TargetPoint targetPoint) {
 | |
| 			Iterator<Pair<Climate.ParameterPoint, T>> iterator = this.values().iterator();
 | |
| 			Pair<Climate.ParameterPoint, T> pair = (Pair<Climate.ParameterPoint, T>)iterator.next();
 | |
| 			long l = pair.getFirst().fitness(targetPoint);
 | |
| 			T object = pair.getSecond();
 | |
| 
 | |
| 			while (iterator.hasNext()) {
 | |
| 				Pair<Climate.ParameterPoint, T> pair2 = (Pair<Climate.ParameterPoint, T>)iterator.next();
 | |
| 				long m = pair2.getFirst().fitness(targetPoint);
 | |
| 				if (m < l) {
 | |
| 					l = m;
 | |
| 					object = pair2.getSecond();
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return object;
 | |
| 		}
 | |
| 
 | |
| 		public T findValueIndex(Climate.TargetPoint targetPoint) {
 | |
| 			return this.findValueIndex(targetPoint, Climate.RTree.Node::distance);
 | |
| 		}
 | |
| 
 | |
| 		protected T findValueIndex(Climate.TargetPoint targetPoint, Climate.DistanceMetric<T> distanceMetric) {
 | |
| 			return this.index.search(targetPoint, distanceMetric);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public record ParameterPoint(
 | |
| 		Climate.Parameter temperature,
 | |
| 		Climate.Parameter humidity,
 | |
| 		Climate.Parameter continentalness,
 | |
| 		Climate.Parameter erosion,
 | |
| 		Climate.Parameter depth,
 | |
| 		Climate.Parameter weirdness,
 | |
| 		long offset
 | |
| 	) {
 | |
| 		public static final Codec<Climate.ParameterPoint> CODEC = RecordCodecBuilder.create(
 | |
| 			instance -> instance.group(
 | |
| 					Climate.Parameter.CODEC.fieldOf("temperature").forGetter(parameterPoint -> parameterPoint.temperature),
 | |
| 					Climate.Parameter.CODEC.fieldOf("humidity").forGetter(parameterPoint -> parameterPoint.humidity),
 | |
| 					Climate.Parameter.CODEC.fieldOf("continentalness").forGetter(parameterPoint -> parameterPoint.continentalness),
 | |
| 					Climate.Parameter.CODEC.fieldOf("erosion").forGetter(parameterPoint -> parameterPoint.erosion),
 | |
| 					Climate.Parameter.CODEC.fieldOf("depth").forGetter(parameterPoint -> parameterPoint.depth),
 | |
| 					Climate.Parameter.CODEC.fieldOf("weirdness").forGetter(parameterPoint -> parameterPoint.weirdness),
 | |
| 					Codec.floatRange(0.0F, 1.0F).fieldOf("offset").xmap(Climate::quantizeCoord, Climate::unquantizeCoord).forGetter(parameterPoint -> parameterPoint.offset)
 | |
| 				)
 | |
| 				.apply(instance, Climate.ParameterPoint::new)
 | |
| 		);
 | |
| 
 | |
| 		long fitness(Climate.TargetPoint point) {
 | |
| 			return Mth.square(this.temperature.distance(point.temperature))
 | |
| 				+ Mth.square(this.humidity.distance(point.humidity))
 | |
| 				+ Mth.square(this.continentalness.distance(point.continentalness))
 | |
| 				+ Mth.square(this.erosion.distance(point.erosion))
 | |
| 				+ Mth.square(this.depth.distance(point.depth))
 | |
| 				+ Mth.square(this.weirdness.distance(point.weirdness))
 | |
| 				+ Mth.square(this.offset);
 | |
| 		}
 | |
| 
 | |
| 		protected List<Climate.Parameter> parameterSpace() {
 | |
| 			return ImmutableList.of(
 | |
| 				this.temperature, this.humidity, this.continentalness, this.erosion, this.depth, this.weirdness, new Climate.Parameter(this.offset, this.offset)
 | |
| 			);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected static final class RTree<T> {
 | |
| 		private static final int CHILDREN_PER_NODE = 6;
 | |
| 		private final Climate.RTree.Node<T> root;
 | |
| 		private final ThreadLocal<Climate.RTree.Leaf<T>> lastResult = new ThreadLocal();
 | |
| 
 | |
| 		private RTree(Climate.RTree.Node<T> root) {
 | |
| 			this.root = root;
 | |
| 		}
 | |
| 
 | |
| 		public static <T> Climate.RTree<T> create(List<Pair<Climate.ParameterPoint, T>> nodes) {
 | |
| 			if (nodes.isEmpty()) {
 | |
| 				throw new IllegalArgumentException("Need at least one value to build the search tree.");
 | |
| 			} else {
 | |
| 				int i = ((Climate.ParameterPoint)((Pair)nodes.get(0)).getFirst()).parameterSpace().size();
 | |
| 				if (i != 7) {
 | |
| 					throw new IllegalStateException("Expecting parameter space to be 7, got " + i);
 | |
| 				} else {
 | |
| 					List<Climate.RTree.Leaf<T>> list = (List<Climate.RTree.Leaf<T>>)nodes.stream()
 | |
| 						.map(pair -> new Climate.RTree.Leaf<>((Climate.ParameterPoint)pair.getFirst(), pair.getSecond()))
 | |
| 						.collect(Collectors.toCollection(ArrayList::new));
 | |
| 					return new Climate.RTree<>(build(i, list));
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		private static <T> Climate.RTree.Node<T> build(int paramSpaceSize, List<? extends Climate.RTree.Node<T>> children) {
 | |
| 			if (children.isEmpty()) {
 | |
| 				throw new IllegalStateException("Need at least one child to build a node");
 | |
| 			} else if (children.size() == 1) {
 | |
| 				return (Climate.RTree.Node<T>)children.get(0);
 | |
| 			} else if (children.size() <= 6) {
 | |
| 				children.sort(Comparator.comparingLong(node -> {
 | |
| 					long lx = 0L;
 | |
| 
 | |
| 					for (int jx = 0; jx < paramSpaceSize; jx++) {
 | |
| 						Climate.Parameter parameter = node.parameterSpace[jx];
 | |
| 						lx += Math.abs((parameter.min() + parameter.max()) / 2L);
 | |
| 					}
 | |
| 
 | |
| 					return lx;
 | |
| 				}));
 | |
| 				return new Climate.RTree.SubTree<>(children);
 | |
| 			} else {
 | |
| 				long l = Long.MAX_VALUE;
 | |
| 				int i = -1;
 | |
| 				List<Climate.RTree.SubTree<T>> list = null;
 | |
| 
 | |
| 				for (int j = 0; j < paramSpaceSize; j++) {
 | |
| 					sort(children, paramSpaceSize, j, false);
 | |
| 					List<Climate.RTree.SubTree<T>> list2 = bucketize(children);
 | |
| 					long m = 0L;
 | |
| 
 | |
| 					for (Climate.RTree.SubTree<T> subTree : list2) {
 | |
| 						m += cost(subTree.parameterSpace);
 | |
| 					}
 | |
| 
 | |
| 					if (l > m) {
 | |
| 						l = m;
 | |
| 						i = j;
 | |
| 						list = list2;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				sort(list, paramSpaceSize, i, true);
 | |
| 				return new Climate.RTree.SubTree<>(
 | |
| 					(List<? extends Climate.RTree.Node<T>>)list.stream().map(subTreex -> build(paramSpaceSize, Arrays.asList(subTreex.children))).collect(Collectors.toList())
 | |
| 				);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		private static <T> void sort(List<? extends Climate.RTree.Node<T>> children, int paramSpaceSize, int size, boolean absolute) {
 | |
| 			Comparator<Climate.RTree.Node<T>> comparator = comparator(size, absolute);
 | |
| 
 | |
| 			for (int i = 1; i < paramSpaceSize; i++) {
 | |
| 				comparator = comparator.thenComparing(comparator((size + i) % paramSpaceSize, absolute));
 | |
| 			}
 | |
| 
 | |
| 			children.sort(comparator);
 | |
| 		}
 | |
| 
 | |
| 		private static <T> Comparator<Climate.RTree.Node<T>> comparator(int size, boolean absolute) {
 | |
| 			return Comparator.comparingLong(node -> {
 | |
| 				Climate.Parameter parameter = node.parameterSpace[size];
 | |
| 				long l = (parameter.min() + parameter.max()) / 2L;
 | |
| 				return absolute ? Math.abs(l) : l;
 | |
| 			});
 | |
| 		}
 | |
| 
 | |
| 		private static <T> List<Climate.RTree.SubTree<T>> bucketize(List<? extends Climate.RTree.Node<T>> nodes) {
 | |
| 			List<Climate.RTree.SubTree<T>> list = Lists.<Climate.RTree.SubTree<T>>newArrayList();
 | |
| 			List<Climate.RTree.Node<T>> list2 = Lists.<Climate.RTree.Node<T>>newArrayList();
 | |
| 			int i = (int)Math.pow(6.0, Math.floor(Math.log(nodes.size() - 0.01) / Math.log(6.0)));
 | |
| 
 | |
| 			for (Climate.RTree.Node<T> node : nodes) {
 | |
| 				list2.add(node);
 | |
| 				if (list2.size() >= i) {
 | |
| 					list.add(new Climate.RTree.SubTree(list2));
 | |
| 					list2 = Lists.<Climate.RTree.Node<T>>newArrayList();
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (!list2.isEmpty()) {
 | |
| 				list.add(new Climate.RTree.SubTree(list2));
 | |
| 			}
 | |
| 
 | |
| 			return list;
 | |
| 		}
 | |
| 
 | |
| 		private static long cost(Climate.Parameter[] parameters) {
 | |
| 			long l = 0L;
 | |
| 
 | |
| 			for (Climate.Parameter parameter : parameters) {
 | |
| 				l += Math.abs(parameter.max() - parameter.min());
 | |
| 			}
 | |
| 
 | |
| 			return l;
 | |
| 		}
 | |
| 
 | |
| 		static <T> List<Climate.Parameter> buildParameterSpace(List<? extends Climate.RTree.Node<T>> children) {
 | |
| 			if (children.isEmpty()) {
 | |
| 				throw new IllegalArgumentException("SubTree needs at least one child");
 | |
| 			} else {
 | |
| 				int i = 7;
 | |
| 				List<Climate.Parameter> list = Lists.<Climate.Parameter>newArrayList();
 | |
| 
 | |
| 				for (int j = 0; j < 7; j++) {
 | |
| 					list.add(null);
 | |
| 				}
 | |
| 
 | |
| 				for (Climate.RTree.Node<T> node : children) {
 | |
| 					for (int k = 0; k < 7; k++) {
 | |
| 						list.set(k, node.parameterSpace[k].span((Climate.Parameter)list.get(k)));
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return list;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public T search(Climate.TargetPoint targetPoint, Climate.DistanceMetric<T> distanceMetric) {
 | |
| 			long[] ls = targetPoint.toParameterArray();
 | |
| 			Climate.RTree.Leaf<T> leaf = this.root.search(ls, (Climate.RTree.Leaf<T>)this.lastResult.get(), distanceMetric);
 | |
| 			this.lastResult.set(leaf);
 | |
| 			return leaf.value;
 | |
| 		}
 | |
| 
 | |
| 		static final class Leaf<T> extends Climate.RTree.Node<T> {
 | |
| 			final T value;
 | |
| 
 | |
| 			Leaf(Climate.ParameterPoint point, T value) {
 | |
| 				super(point.parameterSpace());
 | |
| 				this.value = value;
 | |
| 			}
 | |
| 
 | |
| 			@Override
 | |
| 			protected Climate.RTree.Leaf<T> search(long[] searchedValues, @Nullable Climate.RTree.Leaf<T> leaf, Climate.DistanceMetric<T> metric) {
 | |
| 				return this;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		abstract static class Node<T> {
 | |
| 			protected final Climate.Parameter[] parameterSpace;
 | |
| 
 | |
| 			protected Node(List<Climate.Parameter> parameters) {
 | |
| 				this.parameterSpace = (Climate.Parameter[])parameters.toArray(new Climate.Parameter[0]);
 | |
| 			}
 | |
| 
 | |
| 			protected abstract Climate.RTree.Leaf<T> search(long[] searchedValues, @Nullable Climate.RTree.Leaf<T> leaf, Climate.DistanceMetric<T> metric);
 | |
| 
 | |
| 			protected long distance(long[] values) {
 | |
| 				long l = 0L;
 | |
| 
 | |
| 				for (int i = 0; i < 7; i++) {
 | |
| 					l += Mth.square(this.parameterSpace[i].distance(values[i]));
 | |
| 				}
 | |
| 
 | |
| 				return l;
 | |
| 			}
 | |
| 
 | |
| 			public String toString() {
 | |
| 				return Arrays.toString(this.parameterSpace);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		static final class SubTree<T> extends Climate.RTree.Node<T> {
 | |
| 			final Climate.RTree.Node<T>[] children;
 | |
| 
 | |
| 			protected SubTree(List<? extends Climate.RTree.Node<T>> parameters) {
 | |
| 				this(Climate.RTree.buildParameterSpace(parameters), parameters);
 | |
| 			}
 | |
| 
 | |
| 			protected SubTree(List<Climate.Parameter> parameters, List<? extends Climate.RTree.Node<T>> children) {
 | |
| 				super(parameters);
 | |
| 				this.children = (Climate.RTree.Node<T>[])children.toArray(new Climate.RTree.Node[0]);
 | |
| 			}
 | |
| 
 | |
| 			@Override
 | |
| 			protected Climate.RTree.Leaf<T> search(long[] searchedValues, @Nullable Climate.RTree.Leaf<T> leaf, Climate.DistanceMetric<T> metric) {
 | |
| 				long l = leaf == null ? Long.MAX_VALUE : metric.distance(leaf, searchedValues);
 | |
| 				Climate.RTree.Leaf<T> leaf2 = leaf;
 | |
| 
 | |
| 				for (Climate.RTree.Node<T> node : this.children) {
 | |
| 					long m = metric.distance(node, searchedValues);
 | |
| 					if (l > m) {
 | |
| 						Climate.RTree.Leaf<T> leaf3 = node.search(searchedValues, leaf2, metric);
 | |
| 						long n = node == leaf3 ? m : metric.distance(leaf3, searchedValues);
 | |
| 						if (l > n) {
 | |
| 							l = n;
 | |
| 							leaf2 = leaf3;
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return leaf2;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public record Sampler(
 | |
| 		DensityFunction temperature,
 | |
| 		DensityFunction humidity,
 | |
| 		DensityFunction continentalness,
 | |
| 		DensityFunction erosion,
 | |
| 		DensityFunction depth,
 | |
| 		DensityFunction weirdness,
 | |
| 		List<Climate.ParameterPoint> spawnTarget
 | |
| 	) {
 | |
| 		public Climate.TargetPoint sample(int x, int y, int z) {
 | |
| 			int i = QuartPos.toBlock(x);
 | |
| 			int j = QuartPos.toBlock(y);
 | |
| 			int k = QuartPos.toBlock(z);
 | |
| 			DensityFunction.SinglePointContext singlePointContext = new DensityFunction.SinglePointContext(i, j, k);
 | |
| 			return Climate.target(
 | |
| 				(float)this.temperature.compute(singlePointContext),
 | |
| 				(float)this.humidity.compute(singlePointContext),
 | |
| 				(float)this.continentalness.compute(singlePointContext),
 | |
| 				(float)this.erosion.compute(singlePointContext),
 | |
| 				(float)this.depth.compute(singlePointContext),
 | |
| 				(float)this.weirdness.compute(singlePointContext)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		public BlockPos findSpawnPosition() {
 | |
| 			return this.spawnTarget.isEmpty() ? BlockPos.ZERO : Climate.findSpawnPosition(this.spawnTarget, this);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	static class SpawnFinder {
 | |
| 		private static final long MAX_RADIUS = 2048L;
 | |
| 		Climate.SpawnFinder.Result result;
 | |
| 
 | |
| 		SpawnFinder(List<Climate.ParameterPoint> points, Climate.Sampler sampler) {
 | |
| 			this.result = getSpawnPositionAndFitness(points, sampler, 0, 0);
 | |
| 			this.radialSearch(points, sampler, 2048.0F, 512.0F);
 | |
| 			this.radialSearch(points, sampler, 512.0F, 32.0F);
 | |
| 		}
 | |
| 
 | |
| 		private void radialSearch(List<Climate.ParameterPoint> point, Climate.Sampler sampler, float max, float min) {
 | |
| 			float f = 0.0F;
 | |
| 			float g = min;
 | |
| 			BlockPos blockPos = this.result.location();
 | |
| 
 | |
| 			while (g <= max) {
 | |
| 				int i = blockPos.getX() + (int)(Math.sin(f) * g);
 | |
| 				int j = blockPos.getZ() + (int)(Math.cos(f) * g);
 | |
| 				Climate.SpawnFinder.Result result = getSpawnPositionAndFitness(point, sampler, i, j);
 | |
| 				if (result.fitness() < this.result.fitness()) {
 | |
| 					this.result = result;
 | |
| 				}
 | |
| 
 | |
| 				f += min / g;
 | |
| 				if (f > Math.PI * 2) {
 | |
| 					f = 0.0F;
 | |
| 					g += min;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		private static Climate.SpawnFinder.Result getSpawnPositionAndFitness(List<Climate.ParameterPoint> points, Climate.Sampler sampler, int x, int z) {
 | |
| 			Climate.TargetPoint targetPoint = sampler.sample(QuartPos.fromBlock(x), 0, QuartPos.fromBlock(z));
 | |
| 			Climate.TargetPoint targetPoint2 = new Climate.TargetPoint(
 | |
| 				targetPoint.temperature(), targetPoint.humidity(), targetPoint.continentalness(), targetPoint.erosion(), 0L, targetPoint.weirdness()
 | |
| 			);
 | |
| 			long l = Long.MAX_VALUE;
 | |
| 
 | |
| 			for (Climate.ParameterPoint parameterPoint : points) {
 | |
| 				l = Math.min(l, parameterPoint.fitness(targetPoint2));
 | |
| 			}
 | |
| 
 | |
| 			long m = Mth.square((long)x) + Mth.square((long)z);
 | |
| 			long n = l * Mth.square(2048L) + m;
 | |
| 			return new Climate.SpawnFinder.Result(new BlockPos(x, 0, z), n);
 | |
| 		}
 | |
| 
 | |
| 		record Result(BlockPos location, long fitness) {
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public record TargetPoint(long temperature, long humidity, long continentalness, long erosion, long depth, long weirdness) {
 | |
| 
 | |
| 		@VisibleForTesting
 | |
| 		protected long[] toParameterArray() {
 | |
| 			return new long[]{this.temperature, this.humidity, this.continentalness, this.erosion, this.depth, this.weirdness, 0L};
 | |
| 		}
 | |
| 	}
 | |
| }
 |