From 16705057e0a897aa40a375cc44669a30a77a45b5 Mon Sep 17 00:00:00 2001 From: dfsek Date: Mon, 29 Dec 2025 18:59:57 -0700 Subject: [PATCH] update most things to use new maybe and either --- .../extrusion/BiomeExtrusionProvider.java | 5 +- .../biome/image/ImageBiomeProvider.java | 5 +- .../biome/pipeline/PipelineBiomeProvider.java | 7 +- .../biome/single/SingleBiomeProvider.java | 13 +-- .../interpolation/ElevationInterpolator.java | 2 +- .../addons/commands/locate/BiomeLocator.java | 104 +++++++++--------- .../commands/locate/LocateCommandAddon.java | 31 +++--- .../api/util/function/FunctionUtils.java | 4 + .../api/util/generic/data/types/Maybe.java | 56 ++++++++++ .../world/biome/generation/BiomeProvider.java | 7 +- .../generation/CachingBiomeProvider.java | 12 +- 11 files changed, 155 insertions(+), 91 deletions(-) diff --git a/common/addons/biome-provider-extrusion/src/main/java/com/dfsek/terra/addons/biome/extrusion/BiomeExtrusionProvider.java b/common/addons/biome-provider-extrusion/src/main/java/com/dfsek/terra/addons/biome/extrusion/BiomeExtrusionProvider.java index df9bc1a94..c0bd350b9 100644 --- a/common/addons/biome-provider-extrusion/src/main/java/com/dfsek/terra/addons/biome/extrusion/BiomeExtrusionProvider.java +++ b/common/addons/biome-provider-extrusion/src/main/java/com/dfsek/terra/addons/biome/extrusion/BiomeExtrusionProvider.java @@ -9,6 +9,7 @@ import com.dfsek.terra.addons.biome.extrusion.api.Extrusion; import com.dfsek.terra.addons.biome.extrusion.utils.ExtrusionPipeline; import com.dfsek.terra.addons.biome.extrusion.utils.ExtrusionPipelineFactory; import com.dfsek.terra.api.util.Column; +import com.dfsek.terra.api.util.generic.data.types.Maybe; import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.generation.BiomeProvider; @@ -39,11 +40,11 @@ public class BiomeExtrusionProvider implements BiomeProvider { public Column getColumn(int x, int z, long seed, int min, int max) { return delegate.getBaseBiome(x, z, seed) .map(base -> (Column) new BaseBiomeColumn(this, base, min, max, x, z, seed)) - .orElseGet(() -> BiomeProvider.super.getColumn(x, z, seed, min, max)); + .get(() -> BiomeProvider.super.getColumn(x, z, seed, min, max)); } @Override - public Optional getBaseBiome(int x, int z, long seed) { + public Maybe getBaseBiome(int x, int z, long seed) { return delegate.getBaseBiome(x, z, seed); } diff --git a/common/addons/biome-provider-image/src/main/java/com/dfsek/terra/addons/biome/image/ImageBiomeProvider.java b/common/addons/biome-provider-image/src/main/java/com/dfsek/terra/addons/biome/image/ImageBiomeProvider.java index 30c00ad6e..2f83d47bf 100644 --- a/common/addons/biome-provider-image/src/main/java/com/dfsek/terra/addons/biome/image/ImageBiomeProvider.java +++ b/common/addons/biome-provider-image/src/main/java/com/dfsek/terra/addons/biome/image/ImageBiomeProvider.java @@ -11,6 +11,7 @@ import java.util.Optional; import com.dfsek.terra.addons.image.colorsampler.ColorSampler; import com.dfsek.terra.addons.image.converter.ColorConverter; +import com.dfsek.terra.api.util.generic.data.types.Maybe; import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.generation.BiomeProvider; @@ -40,8 +41,8 @@ public class ImageBiomeProvider implements BiomeProvider { } @Override - public Optional getBaseBiome(int x, int z, long seed) { - return Optional.of(getBiome(x, z)); + public Maybe getBaseBiome(int x, int z, long seed) { + return Maybe.just(getBiome(x, z)); } @Override diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/PipelineBiomeProvider.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/PipelineBiomeProvider.java index 5344cf8fe..8bcc091be 100644 --- a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/PipelineBiomeProvider.java +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/PipelineBiomeProvider.java @@ -8,6 +8,9 @@ package com.dfsek.terra.addons.biome.pipeline; import com.dfsek.seismic.type.sampler.Sampler; + +import com.dfsek.terra.api.util.generic.data.types.Maybe; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; @@ -106,8 +109,8 @@ public class PipelineBiomeProvider implements BiomeProvider { } @Override - public Optional getBaseBiome(int x, int z, long seed) { - return Optional.of(getBiome(x, z, seed)); + public Maybe getBaseBiome(int x, int z, long seed) { + return Maybe.just(getBiome(x, z, seed)); } @Override diff --git a/common/addons/biome-provider-single/src/main/java/com/dfsek/terra/addons/biome/single/SingleBiomeProvider.java b/common/addons/biome-provider-single/src/main/java/com/dfsek/terra/addons/biome/single/SingleBiomeProvider.java index 04b16d723..97b87b25c 100644 --- a/common/addons/biome-provider-single/src/main/java/com/dfsek/terra/addons/biome/single/SingleBiomeProvider.java +++ b/common/addons/biome-provider-single/src/main/java/com/dfsek/terra/addons/biome/single/SingleBiomeProvider.java @@ -8,18 +8,13 @@ package com.dfsek.terra.addons.biome.single; import java.util.Collections; -import java.util.Optional; +import com.dfsek.terra.api.util.generic.data.types.Maybe; import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.generation.BiomeProvider; -public class SingleBiomeProvider implements BiomeProvider { - private final Biome biome; - - public SingleBiomeProvider(Biome biome) { - this.biome = biome; - } +public record SingleBiomeProvider(Biome biome) implements BiomeProvider { @Override public Biome getBiome(int x, int y, int z, long seed) { @@ -27,8 +22,8 @@ public class SingleBiomeProvider implements BiomeProvider { } @Override - public Optional getBaseBiome(int x, int z, long seed) { - return Optional.of(biome); + public Maybe getBaseBiome(int x, int z, long seed) { + return Maybe.just(biome); } @Override diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ElevationInterpolator.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ElevationInterpolator.java index 8d9a6cc84..684282871 100644 --- a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ElevationInterpolator.java +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/math/interpolation/ElevationInterpolator.java @@ -30,7 +30,7 @@ public class ElevationInterpolator { gens[x + 1 + smooth][z + 1 + smooth] = provider .getBaseBiome(bx, bz, seed) - .orElseGet(() -> provider.getBiome(bx, 0, bz, seed)) // kind of a hack + .get(() -> provider.getBiome(bx, 0, bz, seed)) // kind of a hack .getContext() .get(noisePropertiesKey); } diff --git a/common/addons/command-locate/src/main/java/com/dfsek/terra/addons/commands/locate/BiomeLocator.java b/common/addons/command-locate/src/main/java/com/dfsek/terra/addons/commands/locate/BiomeLocator.java index 1799f0fc1..3655d3266 100644 --- a/common/addons/command-locate/src/main/java/com/dfsek/terra/addons/commands/locate/BiomeLocator.java +++ b/common/addons/command-locate/src/main/java/com/dfsek/terra/addons/commands/locate/BiomeLocator.java @@ -2,10 +2,13 @@ package com.dfsek.terra.addons.commands.locate; import com.dfsek.seismic.type.vector.Vector2Int; import com.dfsek.seismic.type.vector.Vector3Int; + import com.dfsek.terra.api.util.generic.data.types.Either; +import com.dfsek.terra.api.util.generic.data.types.Maybe; import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.info.WorldProperties; + import org.jetbrains.annotations.NotNull; import java.util.Optional; @@ -14,6 +17,7 @@ import java.util.function.Predicate; import java.util.stream.IntStream; import java.util.stream.Stream; + public class BiomeLocator { /** @@ -27,9 +31,10 @@ public class BiomeLocator { * @param step The search step/increment. Higher values are faster but less accurate. * @param filter The condition to match the biome. * @param search3D If true, searches the entire vertical column at each step. If false, only checks originY. + * * @return An Optional containing the location of the found biome, or empty if not found. */ - public static Optional> search( + public static Maybe> search( @NotNull BiomeProvider provider, @NotNull WorldProperties properties, int originX, @@ -44,64 +49,63 @@ public class BiomeLocator { int maxHeight = properties.getMaxHeight(); // 1. Check the exact center first - Optional> centerResult = check(provider, seed, originX, originZ, step, filter, search3D, minHeight, maxHeight); - if (centerResult.isPresent()) { - return centerResult; - } + return + check(provider, seed, originX, originZ, step, filter, search3D, minHeight, maxHeight) - // 2. Begin Parallel Square Spiral Search - // We iterate rings sequentially to guarantee finding the *nearest* result. - // However, we process all points within a specific ring in parallel. - for (int r = step; r <= radius; r += step) { - final int currentRadius = r; - final int minX = -currentRadius; - final int maxX = currentRadius; - final int minZ = -currentRadius; - final int maxZ = currentRadius; + // 2. Begin Parallel Square Spiral Search + // We iterate rings sequentially to guarantee finding the *nearest* result. + // However, we process all points within a specific ring in parallel. + .or(() -> { + for(int r = step; r <= radius; r += step) { + final int currentRadius = r; + final int minX = -currentRadius; + final int maxX = currentRadius; + final int minZ = -currentRadius; + final int maxZ = currentRadius; - Stream northSide = IntStream.iterate(minX, n -> n < maxX, n -> n + step) - .mapToObj(x -> new int[]{x, minZ}); // Fixed Z (min), varying X + Stream northSide = IntStream.iterate(minX, n -> n < maxX, n -> n + step) + .mapToObj(x -> new int[]{ x, minZ }); // Fixed Z (min), varying X - Stream eastSide = IntStream.iterate(minZ, n -> n < maxZ, n -> n + step) - .mapToObj(z -> new int[]{maxX, z}); // Fixed X (max), varying Z + Stream eastSide = IntStream.iterate(minZ, n -> n < maxZ, n -> n + step) + .mapToObj(z -> new int[]{ maxX, z }); // Fixed X (max), varying Z - Stream southSide = IntStream.iterate(maxX, n -> n > minX, n -> n - step) - .mapToObj(x -> new int[]{x, maxZ}); // Fixed Z (max), varying X + Stream southSide = IntStream.iterate(maxX, n -> n > minX, n -> n - step) + .mapToObj(x -> new int[]{ x, maxZ }); // Fixed Z (max), varying X - Stream westSide = IntStream.iterate(maxZ, n -> n > minZ, n -> n - step) - .mapToObj(z -> new int[]{minX, z}); // Fixed X (min), varying Z + Stream westSide = IntStream.iterate(maxZ, n -> n > minZ, n -> n - step) + .mapToObj(z -> new int[]{ minX, z }); // Fixed X (min), varying Z - Optional> ringResult = Stream.of(northSide, eastSide, southSide, westSide) - .flatMap(Function.identity()) - .parallel() - .map(coords -> check( - provider, - seed, - originX + coords[0], - originZ + coords[1], - step, - filter, - search3D, - minHeight, - maxHeight - )) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); // findFirst() respects encounter order (North -> East -> South -> West) + Optional> ringResult = Stream.of(northSide, eastSide, southSide, westSide) + .flatMap(Function.identity()) + .parallel() + .map(coords -> check( + provider, + seed, + originX + coords[0], + originZ + coords[1], + step, + filter, + search3D, + minHeight, + maxHeight + )) + .flatMap(Maybe::toStream) + .findFirst(); // findFirst() respects encounter order (North -> East -> South -> West) - if (ringResult.isPresent()) { - return ringResult; - } - } + if(ringResult.isPresent()) { + return Maybe.fromOptional(ringResult); + } + } + return Maybe.nothing(); + }); - return Optional.empty(); } /** * Helper to check a specific coordinate column or point. * This logic is executed inside the worker threads. */ - private static Optional> check( + private static Maybe> check( BiomeProvider provider, long seed, int x, @@ -112,14 +116,14 @@ public class BiomeLocator { int minHeight, int maxHeight ) { - if (search3D) { + if(search3D) { // Iterate from bottom to top of the world using the step - for (int y = minHeight; y < maxHeight; y += step) { - if (filter.test(provider.getBiome(x, y, z, seed))) { - return Optional.of(Either.left(Vector3Int.of(x, y, z))); + for(int y = minHeight; y < maxHeight; y += step) { + if(filter.test(provider.getBiome(x, y, z, seed))) { + return Maybe.just(Either.left(Vector3Int.of(x, y, z))); } } - return Optional.empty(); + return Maybe.nothing(); } else { // 2D Mode: Check only the base biome // We use a flatMap approach here to be safe with Optionals inside the stream diff --git a/common/addons/command-locate/src/main/java/com/dfsek/terra/addons/commands/locate/LocateCommandAddon.java b/common/addons/command-locate/src/main/java/com/dfsek/terra/addons/commands/locate/LocateCommandAddon.java index 3d6d86358..11a62ee52 100644 --- a/common/addons/command-locate/src/main/java/com/dfsek/terra/addons/commands/locate/LocateCommandAddon.java +++ b/common/addons/command-locate/src/main/java/com/dfsek/terra/addons/commands/locate/LocateCommandAddon.java @@ -3,6 +3,10 @@ package com.dfsek.terra.addons.commands.locate; import com.dfsek.seismic.type.vector.Vector2Int; import com.dfsek.seismic.type.vector.Vector3Int; + +import com.dfsek.terra.api.util.function.FunctionUtils; +import com.dfsek.terra.api.util.generic.data.types.Maybe; + import org.incendo.cloud.CommandManager; import org.incendo.cloud.component.DefaultValue; import org.incendo.cloud.context.CommandContext; @@ -26,6 +30,8 @@ import com.dfsek.terra.api.util.reflection.TypeKey; import com.dfsek.terra.api.world.World; import com.dfsek.terra.api.world.biome.Biome; +import static com.dfsek.terra.api.util.function.FunctionUtils.collapse; + public class LocateCommandAddon implements AddonInitializer { @Inject @@ -83,7 +89,7 @@ public class LocateCommandAddon implements AddonInitializer { context.sender().sendMessage( "Searching for " + targetBiome.getID() + " within " + radius + " blocks" + modeMsg + "..."); - Optional> result; + Maybe> result; // 3. Execute Search Loop while(true) { @@ -100,7 +106,7 @@ public class LocateCommandAddon implements AddonInitializer { // Exit Conditions: // 1. Found a result - if(result.isPresent()) { + if(result.isJust()) { break; } // 2. Not in auto mode (only run once) @@ -118,22 +124,11 @@ public class LocateCommandAddon implements AddonInitializer { } // 4. Handle Result - if(result.isPresent()) { - Either location = result.get(); - String coords; - - if(location.hasLeft()) { // 3D Result - Vector3Int vec = location.getLeft().get(); - coords = String.format("%d, %d, %d", vec.getX(), vec.getY(), vec.getZ()); - } else { // 2D Result - Vector2Int vec = location.getRight().get(); - coords = String.format("%d, ~, %d", vec.getX(), vec.getZ()); - } - - context.sender().sendMessage("Found " + targetBiome.getID() + " at [" + coords + "]"); - } else { - context.sender().sendMessage("Could not find " + targetBiome.getID() + " within " + radius + " blocks."); - } + context.sender().sendMessage(collapse(result.map(location -> location.collect( + left -> String.format("%d, %d, %d", left.getX(), left.getY(), left.getZ()), + right -> String.format("%d, ~, %d", right.getX(), right.getZ()))) + .map(coords -> "Found " + targetBiome.getID() + " at [" + coords + "]") + .toEither("Could not find " + targetBiome.getID() + " within " + radius + " blocks."))); }) .permission("terra.locate.biome") ); diff --git a/common/api/src/main/java/com/dfsek/terra/api/util/function/FunctionUtils.java b/common/api/src/main/java/com/dfsek/terra/api/util/function/FunctionUtils.java index 4813418ee..8a36eca81 100644 --- a/common/api/src/main/java/com/dfsek/terra/api/util/function/FunctionUtils.java +++ b/common/api/src/main/java/com/dfsek/terra/api/util/function/FunctionUtils.java @@ -22,6 +22,10 @@ public final class FunctionUtils { return (Either) o.map(Either::right).orElseGet(() -> Either.left(de)); } + public static T collapse(Either either) { + return either.collect(Function.identity(), Function.identity()); + } + public static Function> liftTry(Function f) { return s -> { try { diff --git a/common/api/src/main/java/com/dfsek/terra/api/util/generic/data/types/Maybe.java b/common/api/src/main/java/com/dfsek/terra/api/util/generic/data/types/Maybe.java index 4144ff96c..72e09d119 100644 --- a/common/api/src/main/java/com/dfsek/terra/api/util/generic/data/types/Maybe.java +++ b/common/api/src/main/java/com/dfsek/terra/api/util/generic/data/types/Maybe.java @@ -4,7 +4,9 @@ import com.dfsek.terra.api.util.generic.control.Monad; import java.util.Optional; import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.stream.Stream; public interface Maybe extends Monad> { @@ -21,6 +23,11 @@ public interface Maybe extends Monad> { Either toEither(L l); T get(Supplier def); + boolean isJust(); + + @Override + Maybe map(Function map); + default T get(T def) { return get(() -> def); } @@ -29,6 +36,24 @@ public interface Maybe extends Monad> { return bind(ignore -> m); } + Maybe or(Supplier> or); + + default Maybe or(Maybe or) { + return or(() -> or); + } + + default Stream toStream() { + return map(Stream::of).get(Stream.empty()); + } + + default Maybe filter(Predicate filter) { + return bind(o -> filter.test(o) ? this : nothing()); + } + + static Maybe fromOptional(Optional op) { + return op.map(Maybe::just).orElseGet(Maybe::nothing); + } + static Maybe just(T1 t) { record Just(T value) implements Maybe { @@ -47,6 +72,21 @@ public interface Maybe extends Monad> { return value; } + @Override + public boolean isJust() { + return true; + } + + @Override + public Maybe map(Function map) { + return just(map.apply(value)); + } + + @Override + public Maybe or(Supplier> or) { + return this; + } + @Override public Maybe bind(Function>> map) { return (Maybe) map.apply(value); @@ -77,6 +117,22 @@ public interface Maybe extends Monad> { public T get(Supplier def) { return def.get(); } + + @Override + public boolean isJust() { + return false; + } + + @Override + @SuppressWarnings("unchecked") + public Maybe map(Function map) { + return (Maybe) this; + } + + @Override + public Maybe or(Supplier> or) { + return or.get(); + } } return new Nothing<>(); } diff --git a/common/api/src/main/java/com/dfsek/terra/api/world/biome/generation/BiomeProvider.java b/common/api/src/main/java/com/dfsek/terra/api/world/biome/generation/BiomeProvider.java index eedaaa491..822dad1a2 100644 --- a/common/api/src/main/java/com/dfsek/terra/api/world/biome/generation/BiomeProvider.java +++ b/common/api/src/main/java/com/dfsek/terra/api/world/biome/generation/BiomeProvider.java @@ -9,6 +9,9 @@ package com.dfsek.terra.api.world.biome.generation; import com.dfsek.seismic.type.vector.Vector3; import com.dfsek.seismic.type.vector.Vector3Int; + +import com.dfsek.terra.api.util.generic.data.types.Maybe; + import org.jetbrains.annotations.Contract; import java.util.Optional; @@ -64,8 +67,8 @@ public interface BiomeProvider { return getBiome(vector3.getX(), vector3.getY(), vector3.getZ(), seed); } - default Optional getBaseBiome(int x, int z, long seed) { - return Optional.empty(); + default Maybe getBaseBiome(int x, int z, long seed) { + return Maybe.nothing(); } diff --git a/common/api/src/main/java/com/dfsek/terra/api/world/biome/generation/CachingBiomeProvider.java b/common/api/src/main/java/com/dfsek/terra/api/world/biome/generation/CachingBiomeProvider.java index c11775fab..aaeab8a2d 100644 --- a/common/api/src/main/java/com/dfsek/terra/api/world/biome/generation/CachingBiomeProvider.java +++ b/common/api/src/main/java/com/dfsek/terra/api/world/biome/generation/CachingBiomeProvider.java @@ -1,5 +1,7 @@ package com.dfsek.terra.api.world.biome.generation; +import com.dfsek.terra.api.util.generic.data.types.Maybe; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.Scheduler; @@ -25,14 +27,14 @@ public class CachingBiomeProvider implements BiomeProvider, Handle { protected final BiomeProvider delegate; private final int res; private final ThreadLocal>> cache; - private final ThreadLocal>>> baseCache; + private final ThreadLocal>>> baseCache; protected CachingBiomeProvider(BiomeProvider delegate) { this.delegate = delegate; this.res = delegate.resolution(); this.baseCache = ThreadLocal.withInitial(() -> { - LoadingCache> cache = Caffeine + LoadingCache> cache = Caffeine .newBuilder() .executor(CACHE_EXECUTOR) .scheduler(Scheduler.systemScheduler()) @@ -56,7 +58,7 @@ public class CachingBiomeProvider implements BiomeProvider, Handle { } - private Optional sampleBiome(SeededVector2Key vec) { + private Maybe sampleBiome(SeededVector2Key vec) { this.baseCache.get().setLeft(new SeededVector2Key(0, 0, 0)); return this.delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed); } @@ -80,8 +82,8 @@ public class CachingBiomeProvider implements BiomeProvider, Handle { } @Override - public Optional getBaseBiome(int x, int z, long seed) { - Mutable>> cachePair = baseCache.get(); + public Maybe getBaseBiome(int x, int z, long seed) { + Mutable>> cachePair = baseCache.get(); SeededVector2Key mutableKey = cachePair.getLeft(); mutableKey.set(x, z, seed); return cachePair.getRight().get(mutableKey);