diff --git a/FeatureManager b/FeatureManager index 5af4aff..0da0d13 160000 --- a/FeatureManager +++ b/FeatureManager @@ -1 +1 @@ -Subproject commit 5af4aff86ab3e3a362346bb91076d2bdb0206ca7 +Subproject commit 0da0d13c3dc8426b18a55107b42986e9d66387f1 diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/FutureRegion.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/FutureRegion.java new file mode 100644 index 0000000..5ddfb3b --- /dev/null +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/FutureRegion.java @@ -0,0 +1,57 @@ +package com.terraforged.core.region.gen; + +import com.terraforged.core.region.Region; + +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public class FutureRegion implements Future, Callable { + + private final int rx; + private final int rz; + private final RegionGenerator generator; + + private volatile Region result; + + public FutureRegion(int rx, int rz, RegionGenerator generator) { + this.rx = rx; + this.rz = rz; + this.generator = generator; + } + + @Override + public Region call() { + Region region = result; + if (region == null) { + region = generator.generateRegion(rx, rz); + result = region; + } + return region; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return result != null; + } + + @Override + public Region get() { + return call(); + } + + @Override + public Region get(long timeout, TimeUnit unit) { + return get(); + } +} diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/FutureRegionZoom.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/FutureRegionZoom.java new file mode 100644 index 0000000..039f638 --- /dev/null +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/FutureRegionZoom.java @@ -0,0 +1,61 @@ +package com.terraforged.core.region.gen; + +import com.terraforged.core.region.Region; + +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public class FutureRegionZoom implements Future, Callable { + + private final float cx; + private final float cz; + private final float zoom; + private final boolean filters; + private final RegionGenerator generator; + + private volatile Region result; + + public FutureRegionZoom(float cx, float cz, float zoom, boolean filters, RegionGenerator generator) { + this.cx = cx; + this.cz = cz; + this.zoom = zoom; + this.filters = filters; + this.generator = generator; + } + + @Override + public Region call() { + Region region = result; + if (region == null) { + region = generator.generateRegion(cx, cz, zoom, filters); + result = region; + } + return region; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return result != null; + } + + @Override + public Region get() { + return call(); + } + + @Override + public Region get(long timeout, TimeUnit unit) { + return get(); + } +} diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/RegionCache.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/RegionCache.java index 53b1ed8..323417f 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/RegionCache.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/RegionCache.java @@ -73,7 +73,7 @@ public class RegionCache implements RegionExtent, Disposable.Listener { @Override public Region getRegion(int regionX, int regionZ) { - Region region = queueRegion(regionX, regionZ).get(); + Region region = computeRegion(regionX, regionZ).get(); if (queuing) { queueNeighbours(regionX, regionZ); @@ -82,9 +82,14 @@ public class RegionCache implements RegionExtent, Disposable.Listener { return region; } + private CacheEntry computeRegion(int regionX, int regionZ) { + long id = NoiseUtil.seed(regionX, regionZ); + return cache.computeIfAbsent(id, l -> generator.compute(regionX, regionZ)); + } + public CacheEntry queueRegion(int regionX, int regionZ) { long id = NoiseUtil.seed(regionX, regionZ); - return cache.computeIfAbsent(id, l -> generator.generateCached(regionX, regionZ)); + return cache.computeIfAbsent(id, l -> generator.queue(regionX, regionZ)); } private void queueNeighbours(int regionX, int regionZ) { diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/RegionGenerator.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/RegionGenerator.java index 40e1685..83fc3ea 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/RegionGenerator.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/gen/RegionGenerator.java @@ -97,8 +97,20 @@ public class RegionGenerator implements RegionExtent { return CompletableFuture.supplyAsync(() -> generateRegion(centerX, centerZ, zoom, filter), threadPool); } - public CacheEntry generateCached(int regionX, int regionZ) { - return CacheEntry.supplyAsync(() -> generateRegion(regionX, regionZ), threadPool); + public CacheEntry compute(int regionX, int regionZ) { + return CacheEntry.supply(new FutureRegion(regionX, regionZ, this)); + } + + public CacheEntry compute(float centerX, float centerZ, float zoom, boolean filter) { + return CacheEntry.supply(new FutureRegionZoom(centerX, centerZ, zoom, filter, this)); + } + + public CacheEntry queue(int regionX, int regionZ) { + return CacheEntry.supplyAsync(new FutureRegion(regionX, regionZ, this), threadPool); + } + + public CacheEntry queue(float centerX, float centerZ, float zoom, boolean filter) { + return CacheEntry.supplyAsync(new FutureRegionZoom(centerX, centerZ, zoom, filter, this), threadPool); } public Region generateRegion(int regionX, int regionZ) { diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/CacheEntry.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/CacheEntry.java index 73e5ba1..4177126 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/CacheEntry.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/CacheEntry.java @@ -3,14 +3,16 @@ package com.terraforged.core.util.concurrent.cache; import com.terraforged.core.util.concurrent.ThreadPool; import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.Future; public class CacheEntry implements ExpiringEntry { private volatile long timestamp; - private final ForkJoinTask task; + private final Future task; - public CacheEntry(ForkJoinTask task) { + public CacheEntry(Future task) { this.task = task; this.timestamp = System.currentTimeMillis(); } @@ -25,7 +27,19 @@ public class CacheEntry implements ExpiringEntry { } public T get() { - return task.join(); + if (task instanceof ForkJoinTask) { + return ((ForkJoinTask) task).join(); + } + + try { + return task.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + public static CacheEntry supply(Future future) { + return new CacheEntry<>(future); } public static CacheEntry supplyAsync(Callable callable, ThreadPool executor) { diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/River.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/River.java index 3592a7b..6ff08c9 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/River.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/River.java @@ -163,9 +163,8 @@ public class River extends TerrainPopulator { // lerp the position's height to the riverbank height if (cell.value > bankHeight) { cell.value = NoiseUtil.lerp(cell.value, bankHeight, valleyAlpha); - return true; } - return false; + return true; } private boolean carveBanks(Cell cell, float banksAlpha, float bedHeight) { diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeProvider.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeProvider.java index a59f248..0d46c2e 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeProvider.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeProvider.java @@ -63,10 +63,22 @@ public class BiomeProvider extends AbstractBiomeProvider { this.modifierManager = SetupHooks.setup(new BiomeModifierManager(context, biomeMap), context.copy()); } + public TerraContext getContext() { + return context; + } + + public void lookupPos(int x, int z, Cell cell) { + worldLookup.applyCell(cell, x, z); + } + public Cell lookupPos(int x, int z) { return worldLookup.getCell(x, z); } + public boolean canSpawnAt(Cell cell) { + return cell.tag != context.terrain.ocean && cell.tag != context.terrain.deepOcean && cell.value > context.levels.ground; + } + @Override public Biome getNoiseBiome(int x, int y, int z) { x = (x << 2); diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/spawn/SpawnHandler.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/spawn/SpawnHandler.java new file mode 100644 index 0000000..188dca9 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/spawn/SpawnHandler.java @@ -0,0 +1,43 @@ +package com.terraforged.mod.biome.spawn; + +import com.terraforged.mod.Log; +import com.terraforged.mod.biome.provider.BiomeProvider; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.IFeatureConfig; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE) +public class SpawnHandler { + + @SubscribeEvent + public static void createSpawn(WorldEvent.CreateSpawnPosition event) { + if (event.getWorld() instanceof ServerWorld) { + ServerWorld world =(ServerWorld) event.getWorld(); + if (world.getChunkProvider().getChunkGenerator().getBiomeProvider() instanceof BiomeProvider) { + Log.info("Searching for world spawn position"); + BiomeProvider provider = (BiomeProvider) world.getChunkProvider().getChunkGenerator().getBiomeProvider(); + SpawnSearch search = new SpawnSearch(BlockPos.ZERO, provider); + BlockPos spawn = search.get(); + + Log.info("Setting world spawn: {}", spawn); + event.setCanceled(true); + event.getWorld().getWorldInfo().setSpawn(spawn); + + if (event.getSettings().isBonusChestEnabled()) { + Log.info("Generating bonus chest"); + createBonusChest(world, spawn); + } + } + } + } + + private static void createBonusChest(ServerWorld world, BlockPos pos) { + ConfiguredFeature chest = Feature.BONUS_CHEST.withConfiguration(IFeatureConfig.NO_FEATURE_CONFIG); + chest.place(world, world.getChunkProvider().getChunkGenerator(), world.rand, pos); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/spawn/SpawnSearch.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/spawn/SpawnSearch.java new file mode 100644 index 0000000..be5b8cc --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/spawn/SpawnSearch.java @@ -0,0 +1,43 @@ +package com.terraforged.mod.biome.spawn; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.Log; +import com.terraforged.mod.biome.provider.BiomeProvider; +import com.terraforged.mod.command.search.Search; +import net.minecraft.util.math.BlockPos; + +public class SpawnSearch extends Search { + + private final BiomeProvider biomeProvider; + private final Cell cell = new Cell<>(); + + public SpawnSearch(BlockPos center, BiomeProvider biomeProvider) { + super(center, 0, 2048); + this.biomeProvider = biomeProvider; + } + + @Override + public int getSpacing() { + return 64; + } + + @Override + public boolean test(BlockPos pos) { + biomeProvider.lookupPos(pos.getX(), pos.getZ(), cell); + return biomeProvider.canSpawnAt(cell); + } + + @Override + protected BlockPos success(BlockPos.Mutable pos) { + pos.setY(biomeProvider.getContext().levels.scale(cell.value)); + Log.info("Found valid spawn position: {}", pos); + return super.success(pos); + } + + @Override + protected BlockPos fail(BlockPos pos) { + Log.info("Unable to find valid spawn position, defaulting x=0, z=0"); + return new BlockPos(0, biomeProvider.getContext().levels.groundLevel, 0); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java index 33f98b5..9210d43 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java @@ -395,7 +395,7 @@ public class TerraChunkGenerator extends ObfHelperChunkGenerator(BiomeArgType::new)); - ArgumentTypes.register("terraforged:terrain", TerrainArgType.class, new ArgumentSerializer<>(TerrainArgType::new)); + ArgumentTypes.register("terraforged:biome", BiomeArgType.class, new BiomeArgType.Serializer()); + ArgumentTypes.register("terraforged:terrain", TerrainArgType.class, new TerrainArgType.Serializer()); } @SubscribeEvent @@ -91,31 +92,59 @@ public class TerraCommand { } public static void register(CommandDispatcher dispatcher) { - dispatcher.register(command()); + registerSimple(dispatcher); + registerLocate(dispatcher); + PermissionAPI.registerNode(Permissions.QUERY, DefaultPermissionLevel.OP, "Allows use of the query command"); + PermissionAPI.registerNode(Permissions.DATA, DefaultPermissionLevel.OP, "Allows use of the data command"); + PermissionAPI.registerNode(Permissions.DEFAULTS, DefaultPermissionLevel.OP, "Allows use of the defaults command"); + PermissionAPI.registerNode(Permissions.DEBUG, DefaultPermissionLevel.OP, "Allows use of the debug command"); + PermissionAPI.registerNode(Permissions.LOCATE, DefaultPermissionLevel.OP, "Allows use of the locate command"); } - private static LiteralArgumentBuilder command() { - return Commands.literal("terra") - .requires(source -> source.hasPermissionLevel(2)) + private static void registerSimple(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("terra") .then(Commands.literal("query") + .requires(perm(Permissions.QUERY)) .executes(TerraCommand::query)) .then(Commands.literal("data") + .requires(perm(Permissions.DATA)) .then(Commands.literal("dump") .executes(TerraCommand::dump))) .then(Commands.literal("defaults") + .requires(perm(Permissions.DEFAULTS)) .then(Commands.literal("set") .executes(TerraCommand::setDefaults))) .then(Commands.literal("debug") - .executes(TerraCommand::debugBiome)) + .requires(perm(Permissions.DEBUG)) + .executes(TerraCommand::debugBiome))); + } + + private static void registerLocate(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("terra") .then(Commands.literal("locate") + .requires(perm(Permissions.LOCATE)) .then(Commands.argument("biome", BiomeArgType.biome()) - .executes(TerraCommand::findBiome) - .then(Commands.argument("terrain", TerrainArgType.terrain()) - .executes(TerraCommand::findTerrainAndBiome))) + .executes(TerraCommand::findBiome)))); + + dispatcher.register(Commands.literal("terra") + .then(Commands.literal("locate") + .requires(perm(Permissions.LOCATE)) + .then(Commands.argument("terrain", TerrainArgType.terrain()) + .executes(TerraCommand::findTerrain)))); + + dispatcher.register(Commands.literal("terra") + .then(Commands.literal("locate") + .requires(perm(Permissions.LOCATE)) + .then(Commands.argument("biome", BiomeArgType.biome()) + .then(Commands.argument("terrain", TerrainArgType.terrain()) + .executes(TerraCommand::findTerrainAndBiome))))); + + dispatcher.register(Commands.literal("terra") + .then(Commands.literal("locate") + .requires(perm(Permissions.LOCATE)) .then(Commands.argument("terrain", TerrainArgType.terrain()) - .executes(TerraCommand::findTerrain) .then(Commands.argument("biome", BiomeArgType.biome()) - .executes(TerraCommand::findTerrainAndBiome)))); + .executes(TerraCommand::findTerrainAndBiome))))); } private static int query(CommandContext context) throws CommandSyntaxException { @@ -194,7 +223,7 @@ public class TerraCommand { WorldGenerator worldGenerator = terraContext.factory.get(); Search search = new TerrainSearchTask(pos, worldGenerator, target); doSearch(server, playerID, search); - context.getSource().sendFeedback(new StringTextComponent("Searching..."), false); + context.getSource().sendFeedback(new StringTextComponent("Locating terrain..."), false); return Command.SINGLE_SUCCESS; } @@ -213,7 +242,7 @@ public class TerraCommand { IWorldReader reader = context.getSource().asPlayer().getServerWorld(); Search search = new BiomeSearchTask(pos, reader, biome); doSearch(server, playerID, search); - context.getSource().sendFeedback(new StringTextComponent("Searching..."), false); + context.getSource().sendFeedback(new StringTextComponent("Locating biome..."), false); return Command.SINGLE_SUCCESS; } @@ -237,7 +266,7 @@ public class TerraCommand { Search terrainSearch = new TerrainSearchTask(pos, worldGenerator, target); Search search = new BothSearchTask(pos, biomeSearch, terrainSearch); doSearch(server, playerID, search); - context.getSource().sendFeedback(new StringTextComponent("Searching..."), false); + context.getSource().sendFeedback(new StringTextComponent("Locating biome & terrain..."), false); return Command.SINGLE_SUCCESS; } @@ -275,14 +304,31 @@ public class TerraCommand { // the terrain parsed from the command will not be the same instance as used in the // world generator, so find the matching instance by name private static Terrain getTerrainInstance(Terrain find, Terrains terrains) { + // search for exact match first for (Terrain t : terrains.index) { if (t.getName().equals(find.getName())) { return t; } } + // find a mixed terrain as a fallback + for (Terrain t : terrains.index) { + if (t.getName().contains("-") && t.getName().contains(find.getName())) { + return t; + } + } return find; } + private static Predicate perm(String node) { + return source -> { + try { + return PermissionAPI.hasPermission(source.asPlayer(), node); + } catch (Throwable t) { + return source.hasPermissionLevel(2); + } + }; + } + private static BiomeProvider getBiomeProvider(CommandContext context) { return (BiomeProvider) context.getSource().getWorld().getChunkProvider().getChunkGenerator().getBiomeProvider(); } diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/BiomeArgType.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/BiomeArgType.java index 0dfc4a5..0053536 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/BiomeArgType.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/BiomeArgType.java @@ -25,6 +25,7 @@ package com.terraforged.mod.command.arg; +import com.google.gson.JsonObject; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.context.CommandContext; @@ -33,8 +34,9 @@ import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import net.minecraft.command.ISuggestionProvider; +import net.minecraft.command.arguments.IArgumentSerializer; +import net.minecraft.network.PacketBuffer; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; import net.minecraft.util.text.StringTextComponent; import net.minecraft.world.biome.Biome; import net.minecraftforge.registries.ForgeRegistries; @@ -45,9 +47,28 @@ public class BiomeArgType implements ArgumentType { @Override public Biome parse(StringReader reader) throws CommandSyntaxException { - ResourceLocation resourcelocation = ResourceLocation.read(reader); - return Registry.BIOME.getValue(resourcelocation) - .orElseThrow(() -> createException("Invalid biome", "%s is not a valid biome", resourcelocation)); + int cursor = reader.getCursor(); + String raw = reader.getString().substring(cursor); + + if (raw.indexOf(':') == -1) { + reader.setCursor(cursor); + throw createException("Invalid biome", "%s is not a valid biome", raw); + } + + ResourceLocation resourcelocation = ResourceLocation.tryCreate(raw); + if (resourcelocation == null) { + reader.setCursor(cursor); + throw createException("Invalid biome", "%s is not a valid biome", raw); + } + + if (!ForgeRegistries.BIOMES.containsKey(resourcelocation)) { + reader.setCursor(cursor); + throw createException("Invalid biome", "%s is not a valid biome", resourcelocation); + } + + reader.setCursor(reader.getString().length()); + + return ForgeRegistries.BIOMES.getValue(resourcelocation); } @Override @@ -69,4 +90,22 @@ public class BiomeArgType implements ArgumentType { public static Biome getBiome(CommandContext context, String name) { return context.getArgument(name, Biome.class); } + + public static class Serializer implements IArgumentSerializer { + + @Override + public void write(BiomeArgType type, PacketBuffer buffer) { + + } + + @Override + public BiomeArgType read(PacketBuffer buffer) { + return new BiomeArgType(); + } + + @Override + public void write(BiomeArgType type, JsonObject json) { + + } + } } diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/TerrainArgType.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/TerrainArgType.java index 9a06b1c..dc843a0 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/TerrainArgType.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/TerrainArgType.java @@ -25,6 +25,7 @@ package com.terraforged.mod.command.arg; +import com.google.gson.JsonObject; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.context.CommandContext; @@ -32,8 +33,12 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import com.terraforged.core.settings.Settings; import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.core.world.terrain.Terrains; import net.minecraft.command.ISuggestionProvider; +import net.minecraft.command.arguments.IArgumentSerializer; +import net.minecraft.network.PacketBuffer; import net.minecraft.util.text.StringTextComponent; import java.util.List; @@ -42,7 +47,7 @@ import java.util.stream.Collectors; public class TerrainArgType implements ArgumentType { - private final List terrains = Terrain.getRegistered(); + private final List terrains = createTerrainList(); @Override public Terrain parse(StringReader reader) throws CommandSyntaxException { @@ -76,4 +81,26 @@ public class TerrainArgType implements ArgumentType { new StringTextComponent(String.format(message, args)) ); } + + private static List createTerrainList() { + return Terrains.create(new Settings()).index; + } + + public static class Serializer implements IArgumentSerializer { + + @Override + public void write(TerrainArgType type, PacketBuffer buffer) { + + } + + @Override + public TerrainArgType read(PacketBuffer buffer) { + return new TerrainArgType(); + } + + @Override + public void write(TerrainArgType type, JsonObject json) { + + } + } } diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/search/Search.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/search/Search.java index 585bd81..3bc5f1a 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/command/search/Search.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/search/Search.java @@ -38,6 +38,14 @@ public abstract class Search implements Supplier { return 16; } + protected BlockPos success(BlockPos.Mutable pos) { + return pos.toImmutable(); + } + + protected BlockPos fail(BlockPos pos) { + return pos; + } + @Override public BlockPos get() { int radius = maxRadius; @@ -60,7 +68,7 @@ public abstract class Search implements Supplier { pos.setPos(center.getX() + (x * getSpacing()), center.getY(), center.getZ() + (z * getSpacing())); if (center.distanceSq(pos) >= minRadius2) { if (test(pos)) { - return pos.toImmutable(); + return success(pos); } } } @@ -75,7 +83,7 @@ public abstract class Search implements Supplier { z += dz; } - return BlockPos.ZERO; + return fail(BlockPos.ZERO); } public abstract boolean test(BlockPos pos); diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenFeatures.java b/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenFeatures.java index cbe327b..4c1d082 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenFeatures.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenFeatures.java @@ -30,12 +30,24 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.mojang.datafixers.Dynamic; import com.mojang.datafixers.types.JsonOps; +import net.minecraft.util.ResourceLocation; import net.minecraft.world.biome.Biome; import net.minecraft.world.gen.GenerationStage; import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.IFeatureConfig; +import net.minecraft.world.gen.feature.jigsaw.JigsawManager; +import net.minecraft.world.gen.feature.jigsaw.JigsawPattern; +import net.minecraft.world.gen.feature.jigsaw.JigsawPatternRegistry; +import net.minecraft.world.gen.feature.jigsaw.JigsawPiece; +import net.minecraft.world.gen.feature.structure.Structure; import net.minecraftforge.registries.ForgeRegistries; import java.io.File; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Random; public class WorldGenFeatures extends DataGen { @@ -43,7 +55,9 @@ public class WorldGenFeatures extends DataGen { if (dataDir.exists() || dataDir.mkdirs()) { for (Biome biome : ForgeRegistries.BIOMES) { genBiomeFeatures(dataDir, biome); + genBiomeStructures(dataDir, biome); } + genBiomeJigsaws(dataDir); } } @@ -65,4 +79,55 @@ public class WorldGenFeatures extends DataGen { write(root, writer); }); } + + private static void genBiomeStructures(File dir, Biome biome) { + write(new File(dir, getJsonPath("structures", biome.getRegistryName())), writer -> { + JsonObject root = new JsonObject(); + for (Map.Entry> e : Structure.STRUCTURES.entrySet()) { + JsonArray array = new JsonArray(); + IFeatureConfig config = biome.getStructureConfig(e.getValue()); + if (config == null) { + continue; + } + JsonElement element = config.serialize(JsonOps.INSTANCE).getValue(); + JsonObject object = new JsonObject(); + object.addProperty("structure", e.getValue().getRegistryName() + ""); + object.add("config", element); + array.add(object); + } + write(root, writer); + }); + } + + private static void genBiomeJigsaws(File dir) { + Random random = new Random(); + write(new File(dir, "jigsaws.json"), writer -> { + JsonObject root = new JsonObject(); + for (Map.Entry e : getJigsawRegistry().entrySet()) { + JsonArray array = new JsonArray(); + List pieces = e.getValue().getShuffledPieces(random); + for (JigsawPiece piece : pieces) { + JsonElement element = piece.serialize(JsonOps.INSTANCE).getValue(); + array.add(element); + } + root.add(e.getKey().toString(), array); + } + write(root, writer); + }); + } + + private static Map getJigsawRegistry() { + try { + for (Field field : JigsawPatternRegistry.class.getDeclaredFields()) { + if (field.getType() == Map.class) { + field.setAccessible(true); + Object value = field.get(JigsawManager.REGISTRY); + return (Map) value; + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + return Collections.emptyMap(); + } } diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingListener.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingListener.java index d8a4a9b..7836573 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingListener.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingListener.java @@ -39,6 +39,7 @@ import net.minecraft.world.gen.feature.Feature; import net.minecraft.world.gen.feature.NoFeatureConfig; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.event.world.SaplingGrowTreeEvent; +import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @@ -54,7 +55,7 @@ public class SaplingListener { {new Vec3i(-1, 0, 0), new Vec3i(-1, 0, 1), new Vec3i(0, 0, 1)}, }; - @SubscribeEvent + @SubscribeEvent(priority = EventPriority.LOWEST) public static void onTreeGrow(SaplingGrowTreeEvent event) { // ignore if client if (event.getWorld().isRemote()) { diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/StructurePage.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/StructurePage.java index b1582c5..e0a8278 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/StructurePage.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/StructurePage.java @@ -53,6 +53,6 @@ public class StructurePage extends BasePage { @Override public void init(OverlayScreen parent) { Column left = getColumn(0); - addElements(left.left, left.top, left, structureSettings, false, left.scrollPane::addButton, this::update); + addElements(left.left, left.top, left, structureSettings, true, left.scrollPane::addButton, this::update); } } diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java index d2de588..0ccac71 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java @@ -32,6 +32,7 @@ import com.terraforged.core.region.Region; import com.terraforged.core.region.gen.RegionGenerator; import com.terraforged.core.settings.Settings; import com.terraforged.core.util.concurrent.ThreadPool; +import com.terraforged.core.util.concurrent.cache.CacheEntry; import com.terraforged.core.world.GeneratorContext; import com.terraforged.core.world.WorldGeneratorFactory; import com.terraforged.core.world.terrain.Terrain; @@ -48,8 +49,6 @@ import net.minecraft.nbt.CompoundNBT; import java.awt.*; import java.util.Random; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; public class Preview extends Button { @@ -68,7 +67,7 @@ public class Preview extends Button { private int seed; private long lastUpdate = 0L; private Settings settings = new Settings(); - private Future task = null; + private CacheEntry task = null; private Region region = null; private String[] labels = {"Area: ", "Terrain: ", "Biome: "}; @@ -131,10 +130,6 @@ public class Preview extends Button { try { region = task.get(); render(region); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (ExecutionException e) { - e.getCause().printStackTrace(); } finally { task = null; } @@ -195,7 +190,7 @@ public class Preview extends Button { texture.updateDynamicTexture(); } - private Future generate(Settings settings, CompoundNBT prevSettings) { + private CacheEntry generate(Settings settings, CompoundNBT prevSettings) { NBTHelper.deserialize(prevSettings, previewSettings); settings.generator.seed = seed; this.settings = settings; @@ -208,7 +203,7 @@ public class Preview extends Button { .size(FACTOR, 0) .build(); - return renderer.generate(offsetX, offsetZ, 101 - previewSettings.zoom, true); + return renderer.queue(offsetX, offsetZ, 101 - previewSettings.zoom, true); } private void updateLegend(int mx ,int my) { diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle.json index a57b801..791bfe7 100644 --- a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle.json +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle.json @@ -1,6 +1,7 @@ { "biomes": [ "minecraft:jungle", + "minecraft:modified_jungle", "minecraft:bamboo_jungle" ], "match": [ diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle_edge.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle_edge.json index c6dbf0c..638dfc5 100644 --- a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle_edge.json +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle_edge.json @@ -1,6 +1,7 @@ { "biomes": [ - "minecraft:jungle_edge" + "minecraft:jungle_edge", + "minecraft:modified_jungle_edge" ], "match": [ [ diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_plains.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_plains.json index dbc6b7e..777f214 100644 --- a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_plains.json +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_plains.json @@ -1,7 +1,19 @@ { "biomes": [ "minecraft:plains", - "minecraft:river" + "minecraft:sunflower_plains", + "minecraft:river", + "minecraft:frozen_river", + "minecraft:ocean", + "minecraft:deep_ocean", + "minecraft:warm_ocean", + "minecraft:deep_warm_ocean", + "minecraft:lukewarm_ocean", + "minecraft:deep_lukewarm_ocean", + "minecraft:cold_ocean", + "minecraft:deep_cold_ocean", + "minecraft:frozen_ocean", + "minecraft:deep_frozen_ocean" ], "match": [ [