diff --git a/src/api/java/appeng/api/features/IWorldGen.java b/src/api/java/appeng/api/features/IWorldGen.java index 880807ec9..a03fe81d4 100644 --- a/src/api/java/appeng/api/features/IWorldGen.java +++ b/src/api/java/appeng/api/features/IWorldGen.java @@ -24,7 +24,7 @@ package appeng.api.features; import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; +import net.minecraft.world.IWorld; import net.minecraft.world.dimension.Dimension; public interface IWorldGen { @@ -35,7 +35,7 @@ public interface IWorldGen { void disableWorldGenForDimension(WorldGenType type, ResourceLocation dimID); - boolean isWorldGenEnabled(WorldGenType type, World w); + boolean isWorldGenEnabled(WorldGenType type, IWorld w); enum WorldGenType { CERTUS_QUARTZ, CHARGED_CERTUS_QUARTZ, METEORITES diff --git a/src/main/java/appeng/core/Registration.java b/src/main/java/appeng/core/Registration.java index 731da3d2d..cb7900ece 100644 --- a/src/main/java/appeng/core/Registration.java +++ b/src/main/java/appeng/core/Registration.java @@ -202,7 +202,7 @@ import appeng.tile.AEBaseTile; import appeng.tile.crafting.MolecularAssemblerRenderer; import appeng.worldgen.ChargedQuartzOreConfig; import appeng.worldgen.ChargedQuartzOreFeature; -import appeng.worldgen.MeteoriteWorldGen; +import appeng.worldgen.MeteoriteStructure; final class Registration { @@ -648,13 +648,14 @@ final class Registration { } ForgeRegistries.BIOMES.forEach(b -> { - b.addFeature(GenerationStage.Decoration.TOP_LAYER_MODIFICATION, - new ConfiguredFeature>(MeteoriteWorldGen.INSTANCE, - IFeatureConfig.NO_FEATURE_CONFIG)); +// b.addFeature(GenerationStage.Decoration.TOP_LAYER_MODIFICATION, +// new ConfiguredFeature>(MeteoriteWorldGen.INSTANCE, +// IFeatureConfig.NO_FEATURE_CONFIG)); - b.addFeature(GenerationStage.Decoration.UNDERGROUND_ORES, - new ConfiguredFeature>(MeteoriteWorldGen.INSTANCE, - IFeatureConfig.NO_FEATURE_CONFIG)); + b.addStructure(MeteoriteStructure.INSTANCE.withConfiguration(IFeatureConfig.NO_FEATURE_CONFIG)); + b.addFeature(GenerationStage.Decoration.TOP_LAYER_MODIFICATION, + MeteoriteStructure.INSTANCE.withConfiguration(IFeatureConfig.NO_FEATURE_CONFIG) + .withPlacement(Placement.NOPE.configure(IPlacementConfig.NO_PLACEMENT_CONFIG))); if (AEConfig.instance().isFeatureEnabled(AEFeature.CERTUS_QUARTZ_WORLD_GEN)) { BlockState quartzOre = AEApi.instance().definitions().blocks().quartzOre().block().getDefaultState(); @@ -683,7 +684,8 @@ final class Registration { public void registerWorldGen(RegistryEvent.Register> evt) { IForgeRegistry> r = evt.getRegistry(); - r.register(MeteoriteWorldGen.INSTANCE); +// r.register(MeteoriteWorldGen.INSTANCE); + r.register(MeteoriteStructure.INSTANCE); r.register(ChargedQuartzOreFeature.INSTANCE); } diff --git a/src/main/java/appeng/core/features/registries/WorldGenRegistry.java b/src/main/java/appeng/core/features/registries/WorldGenRegistry.java index 43acec58f..4b03b2c7b 100644 --- a/src/main/java/appeng/core/features/registries/WorldGenRegistry.java +++ b/src/main/java/appeng/core/features/registries/WorldGenRegistry.java @@ -21,6 +21,7 @@ package appeng.core.features.registries; import java.util.HashSet; import net.minecraft.util.ResourceLocation; +import net.minecraft.world.IWorld; import net.minecraft.world.World; import net.minecraft.world.dimension.Dimension; @@ -72,7 +73,7 @@ public final class WorldGenRegistry implements IWorldGen { } @Override - public boolean isWorldGenEnabled(final WorldGenType type, final World w) { + public boolean isWorldGenEnabled(final WorldGenType type, final IWorld w) { if (type == null) { throw new IllegalArgumentException("Bad Type Passed"); } @@ -81,8 +82,8 @@ public final class WorldGenRegistry implements IWorldGen { throw new IllegalArgumentException("Bad Provider Passed"); } - ResourceLocation id = w.dimension.getDimension().getType().getRegistryName(); - final boolean isBadProvider = this.types[type.ordinal()].badProviders.contains(w.dimension.getClass()); + ResourceLocation id = w.getDimension().getType().getRegistryName(); + final boolean isBadProvider = this.types[type.ordinal()].badProviders.contains(w.getDimension().getClass()); final boolean isBadDimension = this.types[type.ordinal()].badDimensions.contains(id); final boolean isGoodDimension = this.types[type.ordinal()].enabledDimensions.contains(id); diff --git a/src/main/java/appeng/debug/ToolMeteoritePlacer.java b/src/main/java/appeng/debug/ToolMeteoritePlacer.java index fc6e7e8db..19e24c6c2 100644 --- a/src/main/java/appeng/debug/ToolMeteoritePlacer.java +++ b/src/main/java/appeng/debug/ToolMeteoritePlacer.java @@ -25,11 +25,13 @@ import net.minecraft.network.play.server.SChunkDataPacket; import net.minecraft.util.ActionResultType; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.MutableBoundingBox; import net.minecraft.util.text.StringTextComponent; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.server.ServerWorld; import appeng.items.AEBaseItem; +import appeng.util.Platform; import appeng.worldgen.MeteoritePlacer; import appeng.worldgen.MeteoriteSpawner; import appeng.worldgen.PlacedMeteoriteSettings; @@ -54,15 +56,28 @@ public class ToolMeteoritePlacer extends AEBaseItem { return ActionResultType.PASS; } - final MeteoriteSpawner ms = new MeteoriteSpawner(); - PlacedMeteoriteSettings spawned = ms.trySpawnMeteorite(world, pos); + // See MeteoriteStructure for original code + float coreRadius = (Platform.getRandomFloat() * 6.0f) + 2; + boolean lava = Platform.getRandomFloat() > 0.9f; + + MeteoriteSpawner spawner = new MeteoriteSpawner(); + PlacedMeteoriteSettings spawned = spawner.trySpawnMeteoriteAtSuitableHeight(world, pos, coreRadius, lava); if (spawned == null) { player.sendMessage(new StringTextComponent("Un-suitable Location.")); return ActionResultType.FAIL; } - final MeteoritePlacer placer = new MeteoritePlacer(world, spawned); + player.sendMessage(new StringTextComponent("Spawned at y=" + spawned.getPos().getY())); + + // Since we don't know yet if the meteorite will be underground or not, + // we have to assume maximum size + int range = (int) Math.ceil((coreRadius * 2 + 5) * 1.25f); + + MutableBoundingBox boundingBox = new MutableBoundingBox(pos.getX() - range, pos.getY(), pos.getZ() - range, + pos.getX() + range, pos.getY(), pos.getZ() + range); + + final MeteoritePlacer placer = new MeteoritePlacer(world, spawned, boundingBox); placer.place(); // The placer will not send chunks to the player since it's used as part diff --git a/src/main/java/appeng/decorative/solid/BlockSkyStone.java b/src/main/java/appeng/decorative/solid/BlockSkyStone.java index 0fe08a17d..a302d09fe 100644 --- a/src/main/java/appeng/decorative/solid/BlockSkyStone.java +++ b/src/main/java/appeng/decorative/solid/BlockSkyStone.java @@ -23,6 +23,7 @@ import net.minecraft.inventory.EquipmentSlotType; import net.minecraft.item.ItemStack; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; import net.minecraft.world.IWorld; import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld; @@ -66,8 +67,8 @@ public class BlockSkyStone extends AEBaseBlock { public BlockState updatePostPlacement(BlockState stateIn, Direction facing, BlockState facingState, IWorld worldIn, BlockPos currentPos, BlockPos facingPos) { if (worldIn instanceof ServerWorld) { - WorldData.instance().compassData().service().updateArea(worldIn, currentPos.getX(), currentPos.getY(), - currentPos.getZ()); + WorldData.instance().compassData().service().updateArea(worldIn, new ChunkPos(currentPos), + currentPos.getY()); } return super.updatePostPlacement(stateIn, facing, facingState, worldIn, currentPos, facingPos); @@ -82,7 +83,7 @@ public class BlockSkyStone extends AEBaseBlock { super.onReplaced(state, w, pos, newState, isMoving); if (w instanceof ServerWorld) { - WorldData.instance().compassData().service().updateArea(w, pos.getX(), pos.getY(), pos.getZ()); + WorldData.instance().compassData().service().updateArea(w, new ChunkPos(pos), pos.getY()); } } diff --git a/src/main/java/appeng/hooks/CompassManager.java b/src/main/java/appeng/hooks/CompassManager.java index 51ac7b42d..cd8c784d4 100644 --- a/src/main/java/appeng/hooks/CompassManager.java +++ b/src/main/java/appeng/hooks/CompassManager.java @@ -21,6 +21,9 @@ package appeng.hooks; import java.util.HashMap; import java.util.Iterator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.server.ServerWorld; + import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.PacketCompassRequest; diff --git a/src/main/java/appeng/server/AECommand.java b/src/main/java/appeng/server/AECommand.java index 0eae4a1b8..cb0033909 100644 --- a/src/main/java/appeng/server/AECommand.java +++ b/src/main/java/appeng/server/AECommand.java @@ -46,11 +46,14 @@ public final class AECommand { private void add(LiteralArgumentBuilder builder, Commands subCommand) { - builder.then(literal(subCommand.name().toLowerCase()).requires(src -> src.hasPermissionLevel(subCommand.level)) - .executes(ctx -> { - subCommand.command.call(ServerLifecycleHooks.getCurrentServer(), new String[0], ctx.getSource()); - return 1; - })); + LiteralArgumentBuilder subCommandBuilder = literal(subCommand.name().toLowerCase()) + .requires(src -> src.hasPermissionLevel(subCommand.level)); + subCommand.command.addArguments(subCommandBuilder); + subCommandBuilder.executes(ctx -> { + subCommand.command.call(ServerLifecycleHooks.getCurrentServer(), ctx, ctx.getSource()); + return 1; + }); + builder.then(subCommandBuilder); } diff --git a/src/main/java/appeng/server/Commands.java b/src/main/java/appeng/server/Commands.java index f088927c2..8b5997d7c 100644 --- a/src/main/java/appeng/server/Commands.java +++ b/src/main/java/appeng/server/Commands.java @@ -20,11 +20,12 @@ package appeng.server; import appeng.server.subcommands.ChunkLogger; import appeng.server.subcommands.Supporters; +import appeng.server.subcommands.TestMeteoritesCommand; import appeng.server.subcommands.TestOreGenCommand; public enum Commands { Chunklogger(4, new ChunkLogger(), false), Supporters(0, new Supporters(), false), - TestOreGen(4, new TestOreGenCommand(), true); + TestOreGen(4, new TestOreGenCommand(), true), TestMeteorites(4, new TestMeteoritesCommand(), true); public final int level; public final ISubCommand command; diff --git a/src/main/java/appeng/server/ISubCommand.java b/src/main/java/appeng/server/ISubCommand.java index 6d29c3892..adc1d885b 100644 --- a/src/main/java/appeng/server/ISubCommand.java +++ b/src/main/java/appeng/server/ISubCommand.java @@ -18,10 +18,16 @@ package appeng.server; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; + import net.minecraft.command.CommandSource; import net.minecraft.server.MinecraftServer; public interface ISubCommand { - void call(MinecraftServer srv, String[] args, CommandSource sender); + default void addArguments(LiteralArgumentBuilder builder) { + } + + void call(MinecraftServer srv, CommandContext ctx, CommandSource sender); } diff --git a/src/main/java/appeng/server/subcommands/ChunkLogger.java b/src/main/java/appeng/server/subcommands/ChunkLogger.java index 3ec142e69..abad21e4c 100644 --- a/src/main/java/appeng/server/subcommands/ChunkLogger.java +++ b/src/main/java/appeng/server/subcommands/ChunkLogger.java @@ -18,6 +18,8 @@ package appeng.server.subcommands; +import com.mojang.brigadier.context.CommandContext; + import net.minecraft.command.CommandSource; import net.minecraft.server.MinecraftServer; import net.minecraft.util.text.TranslationTextComponent; @@ -65,7 +67,7 @@ public class ChunkLogger implements ISubCommand { } @Override - public void call(final MinecraftServer srv, final String[] data, final CommandSource sender) { + public void call(final MinecraftServer srv, final CommandContext data, final CommandSource sender) { this.enabled = !this.enabled; if (this.enabled) { diff --git a/src/main/java/appeng/server/subcommands/Supporters.java b/src/main/java/appeng/server/subcommands/Supporters.java index d0aa7d0dc..514a9d913 100644 --- a/src/main/java/appeng/server/subcommands/Supporters.java +++ b/src/main/java/appeng/server/subcommands/Supporters.java @@ -19,6 +19,7 @@ package appeng.server.subcommands; import com.google.common.base.Joiner; +import com.mojang.brigadier.context.CommandContext; import net.minecraft.command.CommandSource; import net.minecraft.server.MinecraftServer; @@ -29,7 +30,7 @@ import appeng.server.ISubCommand; public class Supporters implements ISubCommand { @Override - public void call(final MinecraftServer srv, final String[] data, final CommandSource sender) { + public void call(final MinecraftServer srv, final CommandContext data, final CommandSource sender) { final String[] who = { "Stig Halvorsen", "Josh Ricker", "Jenny \"Othlon\" Sutherland", "Hristo Bogdanov", "BevoLJ" }; sender.sendFeedback(new StringTextComponent("Special thanks to " + Joiner.on(", ").join(who)), true); diff --git a/src/main/java/appeng/server/subcommands/TestMeteoritesCommand.java b/src/main/java/appeng/server/subcommands/TestMeteoritesCommand.java new file mode 100644 index 000000000..e368edb81 --- /dev/null +++ b/src/main/java/appeng/server/subcommands/TestMeteoritesCommand.java @@ -0,0 +1,177 @@ +/* + * This file is part of Applied Energistics 2. + * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. + * + * Applied Energistics 2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Applied Energistics 2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Applied Energistics 2. If not, see . + */ + +package appeng.server.subcommands; + +import static net.minecraft.command.Commands.literal; + +import java.util.*; + +import com.google.common.math.StatsAccumulator; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import net.minecraft.command.CommandSource; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.feature.structure.StructureStart; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.fml.server.ServerLifecycleHooks; + +import appeng.server.ISubCommand; +import appeng.worldgen.MeteoriteStructure; +import appeng.worldgen.MeteoriteStructurePiece; +import appeng.worldgen.PlacedMeteoriteSettings; + +/** + * This is a testing command to validate quartz ore generation. + */ +public class TestMeteoritesCommand implements ISubCommand { + + @Override + public void addArguments(LiteralArgumentBuilder builder) { + builder.then(literal("force").executes(ctx -> { + test(ServerLifecycleHooks.getCurrentServer(), ctx.getSource(), true); + return 1; + })); + } + + @Override + public void call(final MinecraftServer srv, final CommandContext ctx, final CommandSource sender) { + test(srv, sender, false); + } + + private static void test(MinecraftServer srv, final CommandSource sender, boolean force) { + int radius = 100; + + ServerPlayerEntity player = null; + try { + player = sender.asPlayer(); + } catch (CommandSyntaxException ignored) { + } + ServerWorld world; + BlockPos centerBlock; + if (player != null) { + world = player.getServerWorld(); + centerBlock = new BlockPos(player.getPosX(), 0, player.getPosZ()); + } else { + world = srv.getWorld(DimensionType.OVERWORLD); + centerBlock = world.getSpawnPoint(); + } + + ChunkPos center = new ChunkPos(centerBlock); + + ChunkGenerator generator = world.getChunkProvider().getChunkGenerator(); + + // Find all meteorites in the given rectangle + List found = new ArrayList<>(); + int chunksChecked = 0; + for (int cx = center.x - radius; cx <= center.x + radius; cx++) { + for (int cz = center.z - radius; cz <= center.z + radius; cz++) { + chunksChecked++; + ChunkPos cp = new ChunkPos(cx, cz); + BlockPos p = new BlockPos(cp.getXStart(), 0, cp.getZStart()); + BlockPos nearest = MeteoriteStructure.INSTANCE.findNearest(world, generator, p, 0, false); + if (nearest != null) { + IChunk chunk = world.getChunk(cx, cz, ChunkStatus.STRUCTURE_STARTS); + // The actual relevant information is in the structure piece + MeteoriteStructurePiece piece = getMeteoritePieceFromChunk(chunk); + if (piece != null) { + found.add(piece.getSettings()); + } + } + } + } + + // Create stats on how far apart the meteorites are + StatsAccumulator stats = new StatsAccumulator(); + for (PlacedMeteoriteSettings settings : found) { + double closestOther = Double.NaN; + for (PlacedMeteoriteSettings otherSettings : found) { + if (otherSettings != settings) { + double d = settings.getPos().distanceSq(otherSettings.getPos()); + if (Double.isNaN(closestOther) || d < closestOther) { + closestOther = d; + } + } + } + + if (!Double.isNaN(closestOther)) { + stats.add(Math.sqrt(closestOther)); + } + } + + found.sort(Comparator.comparingDouble(settings -> settings.getPos().distanceSq(centerBlock))); + + sendLine(sender, "Chunks checked: %d", chunksChecked); + sendLine(sender, "Meteorites found: %d", found.size()); + sendLine(sender, "Closest: min=%.2f max=%.2f mean=%.2f stddev=%.2f", stats.min(), stats.max(), stats.mean(), + stats.populationStandardDeviation()); + + int closestCount = Math.min(5, found.size()); + for (int i = 0; i < closestCount; i++) { + PlacedMeteoriteSettings settings = found.get(i); + BlockPos pos = settings.getPos(); + + String state = "not final"; + + if (force && settings.getFallout() == null) { + IChunk chunk = world.getChunk(pos); + MeteoriteStructurePiece piece = getMeteoritePieceFromChunk(chunk); + if (piece == null) { + state = "removed"; + } else { + settings = piece.getSettings(); + pos = settings.getPos(); + } + } + + if (settings.getFallout() == null) { + sendLine(sender, " #%d: pos=%d,%d,%d, radius=%.2f [%s]", i + 1, pos.getX(), pos.getY(), pos.getZ(), + settings.getMeteoriteRadius(), state); + } else { + sendLine(sender, " #%d: pos=%d,%d,%d, radius=%.2f, crater=%s, lava=%s, fallout=%s", i + 1, pos.getX(), + pos.getY(), pos.getZ(), settings.getMeteoriteRadius(), settings.isPlaceCrater(), + settings.isLava(), settings.getFallout().name().toLowerCase()); + } + } + } + + private static MeteoriteStructurePiece getMeteoritePieceFromChunk(IChunk chunk) { + StructureStart start = chunk.getStructureStart(MeteoriteStructure.INSTANCE.getStructureName()); + + if (start != null && start.getComponents().size() > 0 + && start.getComponents().get(0) instanceof MeteoriteStructurePiece) { + return (MeteoriteStructurePiece) start.getComponents().get(0); + } + return null; + } + + private static void sendLine(CommandSource sender, String text, Object... args) { + sender.sendFeedback(new StringTextComponent(String.format(Locale.ROOT, text, args)), true); + } + +} diff --git a/src/main/java/appeng/server/subcommands/TestOreGenCommand.java b/src/main/java/appeng/server/subcommands/TestOreGenCommand.java index 575b66a08..467939d30 100644 --- a/src/main/java/appeng/server/subcommands/TestOreGenCommand.java +++ b/src/main/java/appeng/server/subcommands/TestOreGenCommand.java @@ -25,6 +25,7 @@ import java.util.function.ToDoubleFunction; import java.util.stream.Collectors; import com.google.common.math.StatsAccumulator; +import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.BlockState; @@ -56,7 +57,7 @@ public class TestOreGenCommand implements ISubCommand { } @Override - public void call(final MinecraftServer srv, final String[] data, final CommandSource sender) { + public void call(final MinecraftServer srv, final CommandContext data, final CommandSource sender) { int radius = 1000; @@ -67,7 +68,7 @@ public class TestOreGenCommand implements ISubCommand { world = player.getServerWorld(); center = new BlockPos(player.getPosX(), 0, player.getPosZ()); } catch (CommandSyntaxException e) { - world = srv.getWorld(DimensionType.OVERWORLD); + world = srv.getWorld(DimensionType.OVERWORLD); center = world.getSpawnPoint(); } @@ -83,7 +84,8 @@ public class TestOreGenCommand implements ISubCommand { } AggregatedStats oreCount = AggregatedStats.create(stats.chunks, cs -> (double) cs.quartzOreCount); - List chunksWithOre = stats.chunks.stream().filter(c -> c.quartzOreCount > 0).collect(Collectors.toList()); + List chunksWithOre = stats.chunks.stream().filter(c -> c.quartzOreCount > 0) + .collect(Collectors.toList()); AggregatedStats minHeight = AggregatedStats.create(chunksWithOre, cs -> (double) cs.minHeight); AggregatedStats maxHeight = AggregatedStats.create(chunksWithOre, cs -> (double) cs.maxHeight); AggregatedStats chargedCount = AggregatedStats.create(chunksWithOre, cs -> (double) cs.chargedOreCount); @@ -165,12 +167,8 @@ public class TestOreGenCommand implements ISubCommand { accumulator.add(getter.applyAsDouble(value)); } - return new AggregatedStats( - accumulator.min(), - accumulator.max(), - accumulator.mean(), - accumulator.populationStandardDeviation() - ); + return new AggregatedStats(accumulator.min(), accumulator.max(), accumulator.mean(), + accumulator.populationStandardDeviation()); } @Override @@ -178,8 +176,7 @@ public class TestOreGenCommand implements ISubCommand { if (Double.isNaN(min)) { return "Invalid"; } - return String.format(Locale.ROOT, "min=%.2f, max=%.2f, mean=%.2f, stdDev=%.2f", - min, max, mean, stdDev); + return String.format(Locale.ROOT, "min=%.2f, max=%.2f, mean=%.2f, stdDev=%.2f", min, max, mean, stdDev); } } diff --git a/src/main/java/appeng/services/CompassService.java b/src/main/java/appeng/services/CompassService.java index 4e38e7d20..c246abf6b 100644 --- a/src/main/java/appeng/services/CompassService.java +++ b/src/main/java/appeng/services/CompassService.java @@ -31,6 +31,7 @@ import javax.annotation.Nonnull; import net.minecraft.block.Block; import net.minecraft.server.MinecraftServer; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; import net.minecraft.world.IWorld; import net.minecraft.world.chunk.IChunk; import net.minecraft.world.dimension.DimensionType; @@ -90,27 +91,24 @@ public final class CompassService { } } - public void updateArea(final IWorld w, final int chunkX, final int chunkZ) { - final int x = chunkX << 4; - final int z = chunkZ << 4; + public void updateArea(final IWorld w, ChunkPos chunkPos) { + this.updateArea(w, chunkPos, CHUNK_SIZE); + this.updateArea(w, chunkPos, CHUNK_SIZE + 32); + this.updateArea(w, chunkPos, CHUNK_SIZE + 64); + this.updateArea(w, chunkPos, CHUNK_SIZE + 96); - this.updateArea(w, x, CHUNK_SIZE, z); - this.updateArea(w, x, CHUNK_SIZE + 32, z); - this.updateArea(w, x, CHUNK_SIZE + 64, z); - this.updateArea(w, x, CHUNK_SIZE + 96, z); - - this.updateArea(w, x, CHUNK_SIZE + 128, z); - this.updateArea(w, x, CHUNK_SIZE + 160, z); - this.updateArea(w, x, CHUNK_SIZE + 192, z); - this.updateArea(w, x, CHUNK_SIZE + 224, z); + this.updateArea(w, chunkPos, CHUNK_SIZE + 128); + this.updateArea(w, chunkPos, CHUNK_SIZE + 160); + this.updateArea(w, chunkPos, CHUNK_SIZE + 192); + this.updateArea(w, chunkPos, CHUNK_SIZE + 224); } - public Future updateArea(final IWorld w, final int x, final int y, final int z) { + public Future updateArea(final IWorld w, ChunkPos chunkPos, int y) { this.jobSize++; - final int cx = x >> 4; + final int cx = chunkPos.x; final int cdy = y >> 5; - final int cz = z >> 4; + final int cz = chunkPos.z; final int low_y = cdy << 5; final int hi_y = low_y + 32; diff --git a/src/main/java/appeng/spatial/CachedPlane.java b/src/main/java/appeng/spatial/CachedPlane.java index 76e4b7e54..f9cbf557e 100644 --- a/src/main/java/appeng/spatial/CachedPlane.java +++ b/src/main/java/appeng/spatial/CachedPlane.java @@ -316,8 +316,7 @@ public class CachedPlane { final Chunk c = this.myChunks[x][z]; for (int y = 1; y < 255; y += 32) { - WorldData.instance().compassData().service().updateArea(this.getWorld(), c.getPos().x << 4, y, - c.getPos().z << 4); + WorldData.instance().compassData().service().updateArea(this.getWorld(), c.getPos(), y); } // FIXME this was sending chunks to players... diff --git a/src/main/java/appeng/worldgen/MeteoritePiece.java b/src/main/java/appeng/worldgen/MeteoritePiece.java deleted file mode 100644 index 774c98ee9..000000000 --- a/src/main/java/appeng/worldgen/MeteoritePiece.java +++ /dev/null @@ -1,32 +0,0 @@ -package appeng.worldgen; - -import java.util.Random; - -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.MutableBoundingBox; -import net.minecraft.world.IWorld; -import net.minecraft.world.gen.ChunkGenerator; -import net.minecraft.world.gen.feature.structure.IStructurePieceType; -import net.minecraft.world.gen.feature.structure.ScatteredStructurePiece; -import net.minecraft.world.gen.feature.template.TemplateManager; - -public class MeteoritePiece extends ScatteredStructurePiece { - - public static IStructurePieceType TYPE = IStructurePieceType.register(MeteoritePiece::new, "AE2M"); - - public MeteoritePiece(Random random, int x, int z) { - super(TYPE, random, x, 64, z, 7, 7, 9); - } - - public MeteoritePiece(TemplateManager p_i51340_1_, CompoundNBT p_i51340_2_) { - super(TYPE, p_i51340_2_); - } - - @Override - public boolean create(IWorld worldIn, ChunkGenerator chunkGeneratorIn, Random randomIn, - MutableBoundingBox mutableBoundingBoxIn, ChunkPos chunkPosIn) { - return false; - } - -} diff --git a/src/main/java/appeng/worldgen/MeteoritePlacer.java b/src/main/java/appeng/worldgen/MeteoritePlacer.java index b3d9c267d..5db894715 100644 --- a/src/main/java/appeng/worldgen/MeteoritePlacer.java +++ b/src/main/java/appeng/worldgen/MeteoritePlacer.java @@ -30,9 +30,9 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; -import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MutableBoundingBox; import net.minecraft.world.IWorld; import appeng.api.AEApi; @@ -44,11 +44,7 @@ import appeng.core.AEConfig; import appeng.core.worlddata.WorldData; import appeng.util.InventoryAdaptor; import appeng.util.Platform; -import appeng.worldgen.meteorite.Fallout; -import appeng.worldgen.meteorite.FalloutCopy; -import appeng.worldgen.meteorite.FalloutSand; -import appeng.worldgen.meteorite.FalloutSnow; -import appeng.worldgen.meteorite.MeteoriteBlockPutter; +import appeng.worldgen.meteorite.*; public final class MeteoritePlacer { private static final double PRESSES_SPAWN_CHANCE = 0.7; @@ -67,18 +63,20 @@ public final class MeteoritePlacer { private final double realCrater; private final double squaredMeteoriteSize; private final double crater; - private final int skyMode; + private final boolean placeCrater; private final boolean lava; + private final MutableBoundingBox boundingBox; - public MeteoritePlacer(IWorld world, PlacedMeteoriteSettings settings) { + public MeteoritePlacer(IWorld world, PlacedMeteoriteSettings settings, MutableBoundingBox boundingBox) { + this.boundingBox = boundingBox; this.world = world; this.pos = settings.getPos(); this.x = settings.getPos().getX(); this.y = settings.getPos().getY(); this.z = settings.getPos().getZ(); this.meteoriteSize = settings.getMeteoriteRadius(); - this.realCrater = settings.getCraterRadius(); - this.skyMode = settings.getSkyMode(); + this.realCrater = this.meteoriteSize * 2 + 5; + this.placeCrater = settings.isPlaceCrater(); this.lava = settings.isLava(); this.squaredMeteoriteSize = this.meteoriteSize * this.meteoriteSize; this.crater = this.realCrater * this.realCrater; @@ -89,54 +87,68 @@ public final class MeteoritePlacer { this.skyStone = blocks.skyStoneBlock().block().getDefaultState(); this.skyStoneItem = blocks.skyStoneBlock().item(); - this.type = getFalloutFromBaseBlock(world, settings.getPos(), settings.getBlk()); + this.type = getFallout(world, settings.getPos(), settings.getFallout()); } public void place() { // creator - if (skyMode > 10) { + if (placeCrater) { this.placeCrater(); } this.placeMeteorite(); // collapse blocks... - if (skyMode > 3) { + if (placeCrater) { this.decay(); } } private int minX(int x) { + if (x < boundingBox.minX) { + return boundingBox.minX; + } else if (x > boundingBox.maxX) { + return boundingBox.maxX; + } return x; } private int minZ(int x) { + if (x < boundingBox.minZ) { + return boundingBox.minZ; + } else if (x > boundingBox.maxZ) { + return boundingBox.maxZ; + } return x; } private int maxX(int x) { + if (x < boundingBox.minX) { + return boundingBox.minX; + } else if (x > boundingBox.maxX) { + return boundingBox.maxX; + } return x; } private int maxZ(int x) { + if (x < boundingBox.minZ) { + return boundingBox.minZ; + } else if (x > boundingBox.maxZ) { + return boundingBox.maxZ; + } return x; } private void placeCrater() { final int maxY = 255; - final int minX = minX(x - 200); - final int maxX = maxX(x + 200); - final int minZ = minZ(z - 200); - final int maxZ = maxZ(z + 200); BlockPos.Mutable blockPos = new BlockPos.Mutable(); - for (int j = y - 5; j < maxY; j++) { + for (int j = y - 5; j <= maxY; j++) { blockPos.setY(j); - boolean changed = false; - - for (int i = minX; i < maxX; i++) { + for (int i = boundingBox.minX; i <= boundingBox.maxX; i++) { blockPos.setX(i); - for (int k = minZ; k < maxZ; k++) { + for (int k = boundingBox.minZ; k <= boundingBox.maxZ; k++) { blockPos.setZ(k); final double dx = i - x; final double dz = k - z; @@ -145,12 +157,16 @@ public final class MeteoritePlacer { final double distanceFrom = dx * dx + dz * dz; if (j > h + distanceFrom * 0.02) { - if (lava && j < y && world.getBlockState(blockPos).getMaterial().isSolid()) { + BlockState currentBlock = world.getBlockState(blockPos); + + if (lava && j < y && currentBlock.getMaterial().isSolid()) { if (j > h + distanceFrom * 0.02) { this.putter.put(world, blockPos, Blocks.LAVA.getDefaultState()); } } else { - changed = this.putter.put(world, blockPos, Platform.AIR_BLOCK.getDefaultState()) || changed; + if (!currentBlock.isAir(world, blockPos)) { + this.putter.put(world, blockPos, Blocks.AIR.getDefaultState()); + } } } } @@ -260,11 +276,11 @@ public final class MeteoritePlacer { final int meteorZHeight = maxZ(z + 8); BlockPos.Mutable pos = new BlockPos.Mutable(); - for (int i = meteorXLength; i < meteorXHeight; i++) { + for (int i = meteorXLength; i <= meteorXHeight; i++) { pos.setX(i); for (int j = y - 8; j < y + 8; j++) { pos.setY(j); - for (int k = meteorZLength; k < meteorZHeight; k++) { + for (int k = meteorZLength; k <= meteorZHeight; k++) { pos.setZ(k); final double dx = i - x; final double dy = j - y; @@ -289,11 +305,11 @@ public final class MeteoritePlacer { BlockPos.Mutable blockPos = new BlockPos.Mutable(); BlockPos.Mutable blockPosUp = new BlockPos.Mutable(); BlockPos.Mutable blockPosDown = new BlockPos.Mutable(); - for (int i = meteorXLength; i < meteorXHeight; i++) { + for (int i = meteorXLength; i <= meteorXHeight; i++) { blockPos.setX(i); blockPosUp.setX(i); blockPosDown.setX(i); - for (int k = meteorZLength; k < meteorZHeight; k++) { + for (int k = meteorZLength; k <= meteorZHeight; k++) { blockPos.setZ(k); blockPosUp.setZ(k); blockPosDown.setZ(k); @@ -352,15 +368,17 @@ public final class MeteoritePlacer { } } - private Fallout getFalloutFromBaseBlock(IWorld w, BlockPos pos, ResourceLocation blk) { - if (blk.equals(Blocks.SAND.getRegistryName())) { - return new FalloutSand(w, pos, this.putter, this.skyStone); - } else if (blk.equals(Blocks.TERRACOTTA.getRegistryName())) { - return new FalloutCopy(w, pos, this.putter, this.skyStone); - } else if (blk.equals(Blocks.ICE.getRegistryName()) || blk.equals(Blocks.SNOW.getRegistryName())) { - return new FalloutSnow(w, pos, this.putter, this.skyStone); - } else { - return new Fallout(this.putter, this.skyStone); + private Fallout getFallout(IWorld w, BlockPos pos, FalloutMode mode) { + switch (mode) { + case SAND: + return new FalloutSand(w, pos, this.putter, this.skyStone); + case TERRACOTTA: + return new FalloutCopy(w, pos, this.putter, this.skyStone); + case ICE_SNOW: + return new FalloutSnow(w, pos, this.putter, this.skyStone); + case DEFAULT: + default: + return new Fallout(this.putter, this.skyStone); } } diff --git a/src/main/java/appeng/worldgen/MeteoriteSpawnData.java b/src/main/java/appeng/worldgen/MeteoriteSpawnData.java index e05e799ed..299d59292 100644 --- a/src/main/java/appeng/worldgen/MeteoriteSpawnData.java +++ b/src/main/java/appeng/worldgen/MeteoriteSpawnData.java @@ -15,6 +15,9 @@ import net.minecraft.world.server.ServerWorld; import net.minecraft.world.storage.WorldSavedData; import net.minecraftforge.fml.server.ServerLifecycleHooks; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; + /** * This is per-world data to track where Meteorites have been spawned. */ @@ -28,17 +31,17 @@ public final class MeteoriteSpawnData { this.data = data; } - public void setGenerated(int chunkX, int chunkZ) { + public void setGenerated(ChunkPos chunkPos) { synchronized (data) { // edit. - data.generated.putBoolean(chunkX + "," + chunkZ, true); + data.generated.add(chunkPos.asLong()); data.markDirty(); } } - public boolean hasGenerated(int chunkX, int chunkZ) { + public boolean hasGenerated(ChunkPos pos) { synchronized (data) { - return data.generated.getBoolean(chunkX + "," + chunkZ); + return data.generated.contains(pos.asLong()); } } @@ -148,24 +151,24 @@ public final class MeteoriteSpawnData { private static class SavedData extends WorldSavedData { - private CompoundNBT generated; + private LongOpenHashSet generated; private CompoundNBT spawns; public SavedData() { super(NAME); - this.generated = new CompoundNBT(); + this.generated = new LongOpenHashSet(); this.spawns = new CompoundNBT(); } @Override public synchronized void read(CompoundNBT nbt) { - this.generated = nbt.getCompound("generated"); + this.generated = new LongOpenHashSet(nbt.getLongArray("generated")); this.spawns = nbt.getCompound("spawns"); } @Override public synchronized CompoundNBT write(CompoundNBT compound) { - compound.put("generated", generated); + compound.putLongArray("generated", generated.toLongArray()); compound.put("spawns", spawns); return compound; } diff --git a/src/main/java/appeng/worldgen/MeteoriteSpawner.java b/src/main/java/appeng/worldgen/MeteoriteSpawner.java index 00a863835..a4249386a 100644 --- a/src/main/java/appeng/worldgen/MeteoriteSpawner.java +++ b/src/main/java/appeng/worldgen/MeteoriteSpawner.java @@ -1,99 +1,85 @@ package appeng.worldgen; -import java.util.HashSet; -import java.util.Set; - import javax.annotation.Nullable; import net.minecraft.block.Block; +import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; +import net.minecraft.block.material.Material; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.Tag; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IWorldReader; +import net.minecraft.world.gen.Heightmap; -import appeng.api.AEApi; -import appeng.api.definitions.IBlocks; +import appeng.core.AppEng; +import appeng.worldgen.meteorite.FalloutMode; /** * Makes decisions about spawning meteorites in the world. */ public class MeteoriteSpawner { - private final Set validSpawn = new HashSet<>(); - private final Set invalidSpawn = new HashSet<>(); + private static final ResourceLocation TAG_VALID_SPAWN = new ResourceLocation(AppEng.MOD_ID, + "meteorite/valid_spawn"); + + private static final ResourceLocation TAG_INVALID_SPAWN = new ResourceLocation(AppEng.MOD_ID, + "meteorite/invalid_spawn"); + + private final Tag validSpawnTag; + + private final Tag invalidSpawnTag; + + private final Tag sandTag; public MeteoriteSpawner() { - final IBlocks blocks = AEApi.instance().definitions().blocks(); - - this.validSpawn.add(Blocks.STONE); - this.validSpawn.add(Blocks.COBBLESTONE); - this.validSpawn.add(Blocks.GRASS); - this.validSpawn.add(Blocks.SAND); - this.validSpawn.add(Blocks.DIRT); - this.validSpawn.add(Blocks.GRAVEL); - this.validSpawn.add(Blocks.NETHERRACK); - this.validSpawn.add(Blocks.IRON_ORE); - this.validSpawn.add(Blocks.GOLD_ORE); - this.validSpawn.add(Blocks.DIAMOND_ORE); - this.validSpawn.add(Blocks.REDSTONE_ORE); - this.validSpawn.add(Blocks.ICE); - this.validSpawn.add(Blocks.SNOW); - - this.invalidSpawn.add(blocks.skyStoneBlock().block()); - this.invalidSpawn.add(Blocks.IRON_DOOR); - this.invalidSpawn.add(Blocks.IRON_BARS); - this.invalidSpawn.add(Blocks.OAK_DOOR); - this.invalidSpawn.add(Blocks.ACACIA_DOOR); - this.invalidSpawn.add(Blocks.BIRCH_DOOR); - this.invalidSpawn.add(Blocks.DARK_OAK_DOOR); - this.invalidSpawn.add(Blocks.JUNGLE_DOOR); - this.invalidSpawn.add(Blocks.SPRUCE_DOOR); - this.invalidSpawn.add(Blocks.BRICKS); - this.invalidSpawn.add(Blocks.CLAY); - this.invalidSpawn.add(Blocks.WATER); + this.validSpawnTag = BlockTags.getCollection().getOrCreate(TAG_VALID_SPAWN); + this.invalidSpawnTag = BlockTags.getCollection().getOrCreate(TAG_INVALID_SPAWN); + this.sandTag = BlockTags.getCollection().getOrCreate(new ResourceLocation("minecraft:sand")); } - public PlacedMeteoriteSettings trySpawnMeteoriteAtSuitableHeight(IWorldReader world, int x, int y, int z) { + public PlacedMeteoriteSettings trySpawnMeteoriteAtSuitableHeight(IWorldReader world, BlockPos startPos, + float coreRadius, boolean lava) { - for (int tries = 0; tries < 20; tries++) { - PlacedMeteoriteSettings spawned = trySpawnMeteorite(world, new BlockPos(x, y, z)); + int stepSize = Math.min(5, (int) Math.ceil(coreRadius) + 1); + int minY = 10 + stepSize; + BlockPos.Mutable mutablePos = new BlockPos.Mutable(startPos); + + int startY = world.getHeight(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, startPos).getY(); + mutablePos.setY(startY + stepSize); + + while (mutablePos.getY() > minY) { + PlacedMeteoriteSettings spawned = trySpawnMeteorite(world, mutablePos, coreRadius, lava); if (spawned != null) { return spawned; } - y -= 15; - if (y < 40) { - return null; - } + mutablePos.setY(mutablePos.getY() - stepSize); } return null; } @Nullable - public PlacedMeteoriteSettings trySpawnMeteorite(IWorldReader world, BlockPos pos) { - Block blk = world.getBlockState(pos).getBlock(); - if (!this.validSpawn.contains(blk)) { - return null; // must spawn on a valid block.. - } - - double meteoriteSize = (Math.random() * 6.0) + 2; - double realCrater = meteoriteSize * 2 + 5; - boolean lava = Math.random() > 0.9; - + public PlacedMeteoriteSettings trySpawnMeteorite(IWorldReader world, BlockPos pos, float coreRadius, boolean lava) { if (!areSurroundingsSuitable(world, pos)) { return null; } // we can spawn here! int skyMode = countBlockWithSkyLight(world, pos); + boolean placeCrater = skyMode > 10; boolean solid = !isAirBelowSpawnPoint(world, pos); if (!solid) { - skyMode = 0; + placeCrater = false; } - return new PlacedMeteoriteSettings(pos, blk.getRegistryName(), lava, skyMode, meteoriteSize, realCrater); + FalloutMode fallout = getFalloutFromBaseBlock(world.getBlockState(pos)); + + return new PlacedMeteoriteSettings(pos, coreRadius, lava, placeCrater, fallout); } private static boolean isAirBelowSpawnPoint(IWorldReader w, BlockPos pos) { @@ -136,8 +122,8 @@ public class MeteoriteSpawner { testPos.setY(j); for (int k = pos.getZ() - 6; k < pos.getZ() + 6; k++) { testPos.setZ(k); - Block testBlk = w.getBlockState(testPos).getBlock(); - if (this.validSpawn.contains(testBlk)) { + Block block = w.getBlockState(testPos).getBlock(); + if (block.isIn(validSpawnTag)) { realValidBlocks++; } } @@ -152,10 +138,10 @@ public class MeteoriteSpawner { for (int k = pos.getZ() - 15; k < pos.getZ() + 15; k++) { testPos.setZ(k); Block testBlk = w.getBlockState(testPos).getBlock(); - if (this.invalidSpawn.contains(testBlk)) { + if (testBlk.isIn(invalidSpawnTag)) { return false; } - if (this.validSpawn.contains(testBlk)) { + if (testBlk.isIn(validSpawnTag)) { validBlocks++; } } @@ -166,4 +152,17 @@ public class MeteoriteSpawner { return validBlocks > minBlocks && realValidBlocks > 80; } + private FalloutMode getFalloutFromBaseBlock(BlockState blockState) { + if (blockState.getBlock().isIn(sandTag)) { + return FalloutMode.SAND; + } else if (blockState.getBlock() == Blocks.TERRACOTTA) { + return FalloutMode.TERRACOTTA; + } else if (blockState.getMaterial() == Material.SNOW || blockState.getMaterial() == Material.SNOW_BLOCK + || blockState.getMaterial() == Material.ICE || blockState.getMaterial() == Material.PACKED_ICE) { + return FalloutMode.ICE_SNOW; + } else { + return FalloutMode.DEFAULT; + } + } + } diff --git a/src/main/java/appeng/worldgen/MeteoriteStructure.java b/src/main/java/appeng/worldgen/MeteoriteStructure.java new file mode 100644 index 000000000..d27f11e1e --- /dev/null +++ b/src/main/java/appeng/worldgen/MeteoriteStructure.java @@ -0,0 +1,92 @@ +package appeng.worldgen; + +import java.util.Iterator; +import java.util.Random; +import java.util.function.Function; + +import com.mojang.datafixers.Dynamic; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.MutableBoundingBox; +import net.minecraft.world.IWorld; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeManager; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.GenerationSettings; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.gen.feature.NoFeatureConfig; +import net.minecraft.world.gen.feature.structure.*; +import net.minecraft.world.gen.feature.template.TemplateManager; + +import appeng.core.AEConfig; +import appeng.core.AppEng; + +public class MeteoriteStructure extends ScatteredStructure { + + public static final Structure INSTANCE = new MeteoriteStructure(NoFeatureConfig::deserialize); + + static { + INSTANCE.setRegistryName(AppEng.MOD_ID, "meteorite"); + } + + public MeteoriteStructure(Function, ? extends NoFeatureConfig> configFactoryIn) { + super(configFactoryIn); + } + + @Override + public boolean canBeGenerated(BiomeManager biomeManagerIn, ChunkGenerator generatorIn, Random randIn, int chunkX, + int chunkZ, Biome biomeIn) { + if (super.canBeGenerated(biomeManagerIn, generatorIn, randIn, chunkX, chunkZ, biomeIn)) { + int i = chunkX >> 4; + int j = chunkZ >> 4; + randIn.setSeed((long) (i ^ j << 4) ^ generatorIn.getSeed()); + randIn.nextInt(); + return randIn.nextBoolean(); + } + return false; + } + + @Override + public String getStructureName() { + return INSTANCE.getRegistryName().toString(); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public Structure.IStartFactory getStartFactory() { + return MeteoriteStructure.Start::new; + } + + @Override + protected int getSeedModifier() { + return 124895654; + } + + public static class Start extends StructureStart { + public Start(Structure p_i225815_1_, int p_i225815_2_, int p_i225815_3_, MutableBoundingBox p_i225815_4_, + int p_i225815_5_, long p_i225815_6_) { + super(p_i225815_1_, p_i225815_2_, p_i225815_3_, p_i225815_4_, p_i225815_5_, p_i225815_6_); + } + + public void init(ChunkGenerator generator, TemplateManager templateManagerIn, int chunkX, int chunkZ, + Biome biomeIn) { + + float meteoriteSize = (this.rand.nextFloat() * 6.0f) + 2; + + int centerX = chunkX * 16 + rand.nextInt(16); + int centerZ = chunkZ * 16 + rand.nextInt(16); + + BlockPos actualPos = new BlockPos(centerX, AEConfig.instance().getMeteoriteMaximumSpawnHeight(), centerZ); + + components.add(new MeteoriteStructurePiece(actualPos, meteoriteSize)); + this.recalculateStructureSize(); + } + } + +} diff --git a/src/main/java/appeng/worldgen/MeteoriteStructurePiece.java b/src/main/java/appeng/worldgen/MeteoriteStructurePiece.java new file mode 100644 index 000000000..95c67013d --- /dev/null +++ b/src/main/java/appeng/worldgen/MeteoriteStructurePiece.java @@ -0,0 +1,103 @@ +package appeng.worldgen; + +import java.util.Random; + +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.MutableBoundingBox; +import net.minecraft.world.IWorld; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.feature.structure.IStructurePieceType; +import net.minecraft.world.gen.feature.structure.StructurePiece; +import net.minecraft.world.gen.feature.template.TemplateManager; +import net.minecraftforge.common.util.Constants; + +import appeng.core.worlddata.WorldData; +import appeng.worldgen.meteorite.FalloutMode; + +public class MeteoriteStructurePiece extends StructurePiece { + + public static final IStructurePieceType TYPE = IStructurePieceType.register(MeteoriteStructurePiece::new, + "AE2MTRT"); + + private PlacedMeteoriteSettings settings; + + protected MeteoriteStructurePiece(BlockPos center, float coreRadius) { + super(TYPE, 0); + this.settings = new PlacedMeteoriteSettings(center, coreRadius, false, false, null); + + // Since we don't know yet if the meteorite will be underground or not, + // we have to assume maximum size + int range = (int) Math.ceil((coreRadius * 2 + 5) * 1.25f); + + this.boundingBox = new MutableBoundingBox(center.getX() - range, center.getY(), center.getZ() - range, + center.getX() + range, center.getY(), center.getZ() + range); + } + + public MeteoriteStructurePiece(TemplateManager templateManager, CompoundNBT tag) { + super(TYPE, tag); + + // Mandatory fields + BlockPos center = BlockPos.fromLong(tag.getLong("c")); + float coreRadius = tag.getFloat("r"); + boolean placeCrater = false; + boolean lava = false; + FalloutMode fallout = null; + if (tag.contains("f", Constants.NBT.TAG_COMPOUND)) { + placeCrater = tag.getBoolean("pc"); + lava = tag.getBoolean("l"); + fallout = FalloutMode.values()[tag.getByte("f")]; + } + + this.settings = new PlacedMeteoriteSettings(center, coreRadius, lava, placeCrater, fallout); + } + + public boolean isFinalized() { + return settings.getFallout() != null; + } + + public PlacedMeteoriteSettings getSettings() { + return settings; + } + + @Override + protected void readAdditional(CompoundNBT tag) { + tag.putFloat("r", settings.getMeteoriteRadius()); + tag.putLong("c", settings.getPos().toLong()); + if (isFinalized()) { + tag.putBoolean("pc", settings.isPlaceCrater()); + tag.putBoolean("l", settings.isLava()); + tag.putByte("f", (byte) settings.getFallout().ordinal()); + } + } + + @Override + public boolean create(IWorld world, ChunkGenerator chunkGeneratorIn, Random rand, MutableBoundingBox bounds, + ChunkPos chunkPos) { + + // The parent structure synchronizes on the list of components, so this + // placement should be + // mutually exclusive with any other chunk the structure is placed in. This + // allows us to + // finalize some of the placement parameters now that we have access to an + // actual world object + if (!isFinalized()) { + boolean lava = rand.nextFloat() > 0.9f; + MeteoriteSpawner spawner = new MeteoriteSpawner(); + BlockPos center = settings.getPos(); + float coreRadius = settings.getMeteoriteRadius(); + settings = spawner.trySpawnMeteoriteAtSuitableHeight(world, center, coreRadius, lava); + if (settings == null) { + return false; + } + } + + MeteoritePlacer placer = new MeteoritePlacer(world, settings, bounds); + placer.place(); + + WorldData.instance().compassData().service().updateArea(world, chunkPos); // FIXME: We know the y-range here... + return true; + } + +} diff --git a/src/main/java/appeng/worldgen/MeteoriteWorldGen.java b/src/main/java/appeng/worldgen/MeteoriteWorldGen.java deleted file mode 100644 index 390082069..000000000 --- a/src/main/java/appeng/worldgen/MeteoriteWorldGen.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of Applied Energistics 2. - * Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved. - * - * Applied Energistics 2 is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Applied Energistics 2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Applied Energistics 2. If not, see . - */ - -package appeng.worldgen; - -import java.util.Random; - -import javax.annotation.ParametersAreNonnullByDefault; - -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.IWorld; -import net.minecraft.world.gen.ChunkGenerator; -import net.minecraft.world.gen.GenerationSettings; -import net.minecraft.world.gen.feature.Feature; -import net.minecraft.world.gen.feature.NoFeatureConfig; - -import appeng.api.features.AEFeature; -import appeng.api.features.IWorldGen.WorldGenType; -import appeng.core.AEConfig; -import appeng.core.AppEng; -import appeng.core.features.registries.WorldGenRegistry; -import appeng.core.worlddata.WorldData; -import appeng.util.Platform; - -public final class MeteoriteWorldGen extends Feature { - - public static final MeteoriteWorldGen INSTANCE = new MeteoriteWorldGen(); - - private MeteoriteWorldGen() { - super(NoFeatureConfig::deserialize); - setRegistryName(AppEng.MOD_ID, "meteorite"); - } - - private final MeteoriteSpawner spawner = new MeteoriteSpawner(); - - @ParametersAreNonnullByDefault - @Override - public boolean place(IWorld w, ChunkGenerator generator, Random r, BlockPos pos, - NoFeatureConfig config) { - if (!AEConfig.instance().isFeatureEnabled(AEFeature.METEORITE_WORLD_GEN)) { - return false; - } - - ChunkPos chunkPos = new ChunkPos(pos); - if (!WorldGenRegistry.INSTANCE.isWorldGenEnabled(WorldGenType.METEORITES, w.getWorld())) { - return false; - } - - double minSqDist = Double.MAX_VALUE; - - MeteoriteSpawnData spawnData = MeteoriteSpawnData.get(w); - - // near by meteorites! - for (final PlacedMeteoriteSettings data : spawnData.getNearByMeteorites(chunkPos.x, chunkPos.z)) { - minSqDist = Math.min(minSqDist, getSqDistance(data, pos)); - } - - final boolean isCluster = (minSqDist < 30 * 30) - && Platform.getRandomFloat() < AEConfig.instance().getMeteoriteClusterChance(); - - if (minSqDist > AEConfig.instance().getMinMeteoriteDistanceSq() || isCluster) { - final int x = r.nextInt(16) + (chunkPos.x << 4); - final int y = AEConfig.instance().getMeteoriteMaximumSpawnHeight() + r.nextInt(20); - final int z = r.nextInt(16) + (chunkPos.z << 4); - - PlacedMeteoriteSettings settings = spawner.trySpawnMeteoriteAtSuitableHeight(w, x, y, z); - if (settings != null) { - // Double check that no other chunk generated a meteorite within range while we - // were working - int checkRange = isCluster ? (30 * 30) : AEConfig.instance().getMinMeteoriteDistanceSq(); - if (spawnData.tryAddSpawnedMeteorite(settings, checkRange)) { - MeteoritePlacer placer = new MeteoritePlacer(w, settings); - placer.place(); - WorldData.instance().compassData().service().updateArea(w, chunkPos.x, chunkPos.z); - } - } - } - - spawnData.setGenerated(chunkPos.x, chunkPos.z); - return true; - } - - private static double getSqDistance(PlacedMeteoriteSettings placed, BlockPos pos) { - final int chunkX = placed.getPos().getX() - pos.getX(); - final int chunkZ = placed.getPos().getZ() - pos.getZ(); - return chunkX * chunkX + chunkZ * chunkZ; - } - -} diff --git a/src/main/java/appeng/worldgen/PlacedMeteoriteSettings.java b/src/main/java/appeng/worldgen/PlacedMeteoriteSettings.java index 720efe226..db9e1a119 100644 --- a/src/main/java/appeng/worldgen/PlacedMeteoriteSettings.java +++ b/src/main/java/appeng/worldgen/PlacedMeteoriteSettings.java @@ -1,75 +1,66 @@ package appeng.worldgen; import net.minecraft.nbt.CompoundNBT; -import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; +import appeng.worldgen.meteorite.FalloutMode; + public final class PlacedMeteoriteSettings { private final BlockPos pos; - private final ResourceLocation blk; private final boolean lava; - private final int skyMode; - private final double meteoriteRadius; - private final double craterRadius; + private final boolean placeCrater; + private final float meteoriteRadius; + private final FalloutMode fallout; - public PlacedMeteoriteSettings(BlockPos pos, ResourceLocation blk, boolean lava, int skyMode, - double meteoriteRadius, double craterRadius) { + public PlacedMeteoriteSettings(BlockPos pos, float meteoriteRadius, boolean lava, boolean placeCrater, + FalloutMode fallout) { this.pos = pos; - this.blk = blk; this.lava = lava; - this.skyMode = skyMode; + this.placeCrater = placeCrater; this.meteoriteRadius = meteoriteRadius; - this.craterRadius = craterRadius; + this.fallout = fallout; } public BlockPos getPos() { return pos; } - public ResourceLocation getBlk() { - return blk; - } - public boolean isLava() { return lava; } - public int getSkyMode() { - return skyMode; + public boolean isPlaceCrater() { + return placeCrater; } - public double getMeteoriteRadius() { + public float getMeteoriteRadius() { return meteoriteRadius; } - public double getCraterRadius() { - return craterRadius; + public FalloutMode getFallout() { + return fallout; } public CompoundNBT write(CompoundNBT tag) { - tag.putInt("x", pos.getX()); - tag.putInt("y", pos.getY()); - tag.putInt("z", pos.getZ()); - tag.putString("blk", blk.toString()); + tag.putLong("c", pos.toLong()); - tag.putDouble("meteoriteRadius", meteoriteRadius); - tag.putDouble("craterRadius", craterRadius); + tag.putFloat("mr", meteoriteRadius); - tag.putBoolean("lava", lava); - tag.putInt("skyMode", skyMode); + tag.putBoolean("l", lava); + tag.putBoolean("cr", placeCrater); + tag.putByte("f", (byte) fallout.ordinal()); return tag; } public static PlacedMeteoriteSettings read(CompoundNBT tag) { - BlockPos pos = new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z")); - ResourceLocation blk = new ResourceLocation(tag.getString("blk")); - double meteoriteRadius = tag.getDouble("meteoriteRadius"); - double craterRadius = tag.getDouble("craterRadius"); - boolean lava = tag.getBoolean("lava"); - int skyMode = tag.getInt("skyMode"); + BlockPos pos = BlockPos.fromLong(tag.getLong("c")); + float meteoriteRadius = tag.getFloat("mr"); + boolean lava = tag.getBoolean("l"); + boolean placeCrater = tag.getBoolean("cr"); + FalloutMode fallout = FalloutMode.values()[tag.getByte("f")]; - return new PlacedMeteoriteSettings(pos, blk, lava, skyMode, meteoriteRadius, craterRadius); + return new PlacedMeteoriteSettings(pos, meteoriteRadius, lava, placeCrater, fallout); } } diff --git a/src/main/java/appeng/worldgen/meteorite/FalloutMode.java b/src/main/java/appeng/worldgen/meteorite/FalloutMode.java new file mode 100644 index 000000000..58050b24d --- /dev/null +++ b/src/main/java/appeng/worldgen/meteorite/FalloutMode.java @@ -0,0 +1,5 @@ +package appeng.worldgen.meteorite; + +public enum FalloutMode { + SAND, TERRACOTTA, ICE_SNOW, DEFAULT +} diff --git a/src/main/resources/data/appliedenergistics2/tags/blocks/meteorite/invalid_spawn.json b/src/main/resources/data/appliedenergistics2/tags/blocks/meteorite/invalid_spawn.json new file mode 100644 index 000000000..7ba433f1a --- /dev/null +++ b/src/main/resources/data/appliedenergistics2/tags/blocks/meteorite/invalid_spawn.json @@ -0,0 +1,16 @@ +{ + "values": [ + "appliedenergistics2:sky_stone_block", + "#minecraft:doors", + "minecraft:iron_bars", + "minecraft:bricks", + "minecraft:clay", + "minecraft:water", + "#minecraft:oak_logs", + "#minecraft:acacia_logs", + "#minecraft:birch_logs", + "#minecraft:dark_oak_logs", + "#minecraft:spruce_logs", + "#minecraft:planks" + ] +} diff --git a/src/main/resources/data/appliedenergistics2/tags/blocks/meteorite/valid_spawn.json b/src/main/resources/data/appliedenergistics2/tags/blocks/meteorite/valid_spawn.json new file mode 100644 index 000000000..6d818e07c --- /dev/null +++ b/src/main/resources/data/appliedenergistics2/tags/blocks/meteorite/valid_spawn.json @@ -0,0 +1,16 @@ +{ + "values": [ + "#forge:stone", + "#forge:cobblestone", + "minecraft:grass_block", + "#forge:sand", + "#forge:sandstone", + "#forge:dirt", + "#forge:gravel", + "#forge:netherrack", + "#forge:ores", + "#minecraft:ice", + "minecraft:snow", + "minecraft:snow_block" + ] +}