Merge pull request #164 from TerraForged/master

update branch to latest
This commit is contained in:
dags 2020-05-05 22:12:41 +01:00 committed by GitHub
commit bfb726d2d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 499 additions and 48 deletions

@ -1 +1 @@
Subproject commit 5af4aff86ab3e3a362346bb91076d2bdb0206ca7
Subproject commit 0da0d13c3dc8426b18a55107b42986e9d66387f1

View File

@ -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<Region>, Callable<Region> {
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();
}
}

View File

@ -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<Region>, Callable<Region> {
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();
}
}

View File

@ -73,7 +73,7 @@ public class RegionCache implements RegionExtent, Disposable.Listener<Region> {
@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<Region> {
return region;
}
private CacheEntry<Region> computeRegion(int regionX, int regionZ) {
long id = NoiseUtil.seed(regionX, regionZ);
return cache.computeIfAbsent(id, l -> generator.compute(regionX, regionZ));
}
public CacheEntry<Region> 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) {

View File

@ -97,8 +97,20 @@ public class RegionGenerator implements RegionExtent {
return CompletableFuture.supplyAsync(() -> generateRegion(centerX, centerZ, zoom, filter), threadPool);
}
public CacheEntry<Region> generateCached(int regionX, int regionZ) {
return CacheEntry.supplyAsync(() -> generateRegion(regionX, regionZ), threadPool);
public CacheEntry<Region> compute(int regionX, int regionZ) {
return CacheEntry.supply(new FutureRegion(regionX, regionZ, this));
}
public CacheEntry<Region> compute(float centerX, float centerZ, float zoom, boolean filter) {
return CacheEntry.supply(new FutureRegionZoom(centerX, centerZ, zoom, filter, this));
}
public CacheEntry<Region> queue(int regionX, int regionZ) {
return CacheEntry.supplyAsync(new FutureRegion(regionX, regionZ, this), threadPool);
}
public CacheEntry<Region> 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) {

View File

@ -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<T> implements ExpiringEntry {
private volatile long timestamp;
private final ForkJoinTask<T> task;
private final Future<T> task;
public CacheEntry(ForkJoinTask<T> task) {
public CacheEntry(Future<T> task) {
this.task = task;
this.timestamp = System.currentTimeMillis();
}
@ -25,7 +27,19 @@ public class CacheEntry<T> implements ExpiringEntry {
}
public T get() {
return task.join();
if (task instanceof ForkJoinTask) {
return ((ForkJoinTask<T>) task).join();
}
try {
return task.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public static <T> CacheEntry<T> supply(Future<T> future) {
return new CacheEntry<>(future);
}
public static <T> CacheEntry<T> supplyAsync(Callable<T> callable, ThreadPool executor) {

View File

@ -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<Terrain> cell, float banksAlpha, float bedHeight) {

View File

@ -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<Terrain> cell) {
worldLookup.applyCell(cell, x, z);
}
public Cell<Terrain> lookupPos(int x, int z) {
return worldLookup.getCell(x, z);
}
public boolean canSpawnAt(Cell<Terrain> 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);

View File

@ -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);
}
}

View File

@ -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<Terrain> 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);
}
}

View File

@ -395,7 +395,7 @@ public class TerraChunkGenerator extends ObfHelperChunkGenerator<GenerationSetti
.legacy(context.terraSettings.version == 0)
.pool(ThreadPool.getPool())
.factory(context.factory)
.size(4, 2)
.size(3, 2)
.build()
.toCache(false);
}

View File

@ -0,0 +1,10 @@
package com.terraforged.mod.command;
public class Permissions {
public static final String QUERY = "terraforged.command.terra.query";
public static final String DEBUG = "terraforged.command.terra.debug";
public static final String DATA = "terraforged.command.terra.data";
public static final String DEFAULTS = "terraforged.command.terra.defaults";
public static final String LOCATE = "terraforged.command.terra.locate";
}

View File

@ -27,7 +27,6 @@ package com.terraforged.mod.command;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
@ -49,7 +48,6 @@ import com.terraforged.mod.data.DataGen;
import com.terraforged.mod.settings.SettingsHelper;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.command.arguments.ArgumentSerializer;
import net.minecraft.command.arguments.ArgumentTypes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
@ -70,18 +68,21 @@ import net.minecraft.world.gen.ChunkGenerator;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.server.permission.DefaultPermissionLevel;
import net.minecraftforge.server.permission.PermissionAPI;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.function.Supplier;
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE)
public class TerraCommand {
public static void init() {
ArgumentTypes.register("terraforged:biome", BiomeArgType.class, new ArgumentSerializer<>(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<CommandSource> 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<CommandSource> command() {
return Commands.literal("terra")
.requires(source -> source.hasPermissionLevel(2))
private static void registerSimple(CommandDispatcher<CommandSource> 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<CommandSource> 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<CommandSource> 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<CommandSource> perm(String node) {
return source -> {
try {
return PermissionAPI.hasPermission(source.asPlayer(), node);
} catch (Throwable t) {
return source.hasPermissionLevel(2);
}
};
}
private static BiomeProvider getBiomeProvider(CommandContext<CommandSource> context) {
return (BiomeProvider) context.getSource().getWorld().getChunkProvider().getChunkGenerator().getBiomeProvider();
}

View File

@ -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<Biome> {
@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<Biome> {
public static <S> Biome getBiome(CommandContext<S> context, String name) {
return context.getArgument(name, Biome.class);
}
public static class Serializer implements IArgumentSerializer<BiomeArgType> {
@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) {
}
}
}

View File

@ -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<Terrain> {
private final List<Terrain> terrains = Terrain.getRegistered();
private final List<Terrain> terrains = createTerrainList();
@Override
public Terrain parse(StringReader reader) throws CommandSyntaxException {
@ -76,4 +81,26 @@ public class TerrainArgType implements ArgumentType<Terrain> {
new StringTextComponent(String.format(message, args))
);
}
private static List<Terrain> createTerrainList() {
return Terrains.create(new Settings()).index;
}
public static class Serializer implements IArgumentSerializer<TerrainArgType> {
@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) {
}
}
}

View File

@ -38,6 +38,14 @@ public abstract class Search implements Supplier<BlockPos> {
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<BlockPos> {
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<BlockPos> {
z += dz;
}
return BlockPos.ZERO;
return fail(BlockPos.ZERO);
}
public abstract boolean test(BlockPos pos);

View File

@ -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<String, Structure<?>> 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<ResourceLocation, JigsawPattern> e : getJigsawRegistry().entrySet()) {
JsonArray array = new JsonArray();
List<JigsawPiece> 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<ResourceLocation, JigsawPattern> getJigsawRegistry() {
try {
for (Field field : JigsawPatternRegistry.class.getDeclaredFields()) {
if (field.getType() == Map.class) {
field.setAccessible(true);
Object value = field.get(JigsawManager.REGISTRY);
return (Map<ResourceLocation, JigsawPattern>) value;
}
}
} catch (Throwable t) {
t.printStackTrace();
}
return Collections.emptyMap();
}
}

View File

@ -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()) {

View File

@ -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);
}
}

View File

@ -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<Region> task = null;
private CacheEntry<Region> 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<Region> generate(Settings settings, CompoundNBT prevSettings) {
private CacheEntry<Region> 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) {

View File

@ -1,6 +1,7 @@
{
"biomes": [
"minecraft:jungle",
"minecraft:modified_jungle",
"minecraft:bamboo_jungle"
],
"match": [

View File

@ -1,6 +1,7 @@
{
"biomes": [
"minecraft:jungle_edge"
"minecraft:jungle_edge",
"minecraft:modified_jungle_edge"
],
"match": [
[

View File

@ -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": [
[