diff --git a/src/main/java/org/betterx/betterend/commands/CommandRegistry.java b/src/main/java/org/betterx/betterend/commands/CommandRegistry.java index 4626bd80..d0451cc6 100644 --- a/src/main/java/org/betterx/betterend/commands/CommandRegistry.java +++ b/src/main/java/org/betterx/betterend/commands/CommandRegistry.java @@ -1,21 +1,37 @@ package org.betterx.betterend.commands; +import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiome; +import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI; import org.betterx.bclib.api.v2.poi.BCLPoiType; import org.betterx.bclib.util.BlocksHelper; +import org.betterx.betterend.registry.EndBiomes; import org.betterx.betterend.registry.EndPoiTypes; +import org.betterx.betterend.world.biome.EndBiome; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.math.Vector3d; +import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandBuildContext; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.ResourceOrTagLocationArgument; import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.core.Holder; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.commands.LocateCommand; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.TagKey; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; @@ -23,9 +39,7 @@ import net.minecraft.world.phys.Vec3; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; public class CommandRegistry { public static void register() { @@ -44,6 +58,10 @@ public class CommandRegistry { .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) .executes(ctx -> find_poi(ctx, EndPoiTypes.ETERNAL_PORTAL_INACTIVE)) ) + .then(Commands.literal("tpnext") + .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) + .executes(ctx -> teleportToNextBiome(ctx)) + ) ); } @@ -92,5 +110,103 @@ public class CommandRegistry { } return Command.SINGLE_SUCCESS; } + + + private static int biomeIndex = 0; + private static final int MAX_SEARCH_RADIUS = 6400 * 2; + private static final int SAMPLE_RESOLUTION_HORIZONTAL = 32; + private static final int SAMPLE_RESOLUTION_VERTICAL = 64; + private static final DynamicCommandExceptionType ERROR_BIOME_NOT_FOUND = new DynamicCommandExceptionType( + (object) -> { + return Component.literal("The next biome (" + object + ") was not found."); + }); + + private static int teleportToNextBiome(CommandContext ctx) throws CommandSyntaxException { + final CommandSourceStack source = ctx.getSource(); + List biomes = EndBiomes.ALL_BE_BIOMES; + + if (biomeIndex < 0 || biomeIndex >= biomes.size()) { + source.sendFailure(Component.literal("Failed to find the next Biome...") + .setStyle(Style.EMPTY.withColor(ChatFormatting.RED))); + return 0; + } + final BCLBiome biome = biomes.get(biomeIndex); + source.sendSuccess(Component.literal("Locating Biome " + biome) + .setStyle(Style.EMPTY.withColor(ChatFormatting.DARK_GREEN)), false); + biomeIndex = (biomeIndex + 1) % biomes.size(); + + final BlockPos currentPosition = new BlockPos(source.getPosition()); + final BlockPos biomePosition = source.getLevel() + .findClosestBiome3d( + b -> b.unwrapKey().orElseThrow().location().equals(biome.getID()), + currentPosition, + MAX_SEARCH_RADIUS, + SAMPLE_RESOLUTION_HORIZONTAL, + SAMPLE_RESOLUTION_VERTICAL + ) + .getFirst(); + final String biomeName = biome.toString(); + + if (biomePosition == null) { + throw ERROR_BIOME_NOT_FOUND.create(biomeName); + } else { + final ServerPlayer player = source.getPlayerOrException(); + BlockState state; + BlockPos target; + double yPos = source.getPosition().y(); + boolean didWrap = false; + do { + target = new BlockPos(biomePosition.getX(), yPos, biomePosition.getZ()); + state = player.level.getBlockState(target); + yPos--; + if (yPos <= player.level.getMinBuildHeight() + 1) { + if (didWrap) break; + yPos = 127; + didWrap = true; + } + } while (!state.isAir() && yPos > player.level.getMinBuildHeight() && yPos < player.level.getMaxBuildHeight()); + Vector3d targetPlayerPos = new Vector3d(target.getX() + 0.5, target.getY() - 1, target.getZ() + 0.5); + + player.connection.teleport( + targetPlayerPos.x, + targetPlayerPos.y, + targetPlayerPos.z, + 0, + 0, + Collections.EMPTY_SET + ); + ResourceOrTagLocationArgument.Result result = new ResourceOrTagLocationArgument.Result() { + @Override + public Either unwrap() { + return Either.left(BiomeAPI.getBiomeKey(biome.getBiome())); + } + + @Override + public Optional cast(ResourceKey resourceKey) { + return Optional.empty(); + } + + @Override + public String asPrintable() { + return biomeName; + } + + @Override + public boolean test(Object o) { + return false; + } + }; + ResourceKey a = BiomeAPI.getBiomeKey(biome.getBiome()); + Holder h = BuiltinRegistries.BIOME.getHolder(a).orElseThrow(); + return LocateCommand.showLocateResult( + source, + result, + currentPosition, + new Pair<>(biomePosition, h), + "commands.locatebiome.success", + false + ); + } + } } diff --git a/src/main/java/org/betterx/betterend/registry/EndBiomes.java b/src/main/java/org/betterx/betterend/registry/EndBiomes.java index 8eaee62e..e28e3891 100644 --- a/src/main/java/org/betterx/betterend/registry/EndBiomes.java +++ b/src/main/java/org/betterx/betterend/registry/EndBiomes.java @@ -18,9 +18,12 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.biome.Biome; +import java.util.LinkedList; +import java.util.List; + public class EndBiomes { public static final BiomeAPI.BiomeType END_CAVE = new BiomeAPI.BiomeType("END_CAVE", BiomeAPI.BiomeType.END); - + public static final List ALL_BE_BIOMES = new LinkedList<>(); public static BiomePicker CAVE_BIOMES = null; private static HexBiomeMap caveBiomeMap; private static long lastSeed; @@ -113,6 +116,7 @@ public class EndBiomes { } else { BiomeAPI.registerEndVoidBiome(biome); } + ALL_BE_BIOMES.add(biome); } return biome; } @@ -150,6 +154,7 @@ public class EndBiomes { final EndCaveBiome biome = EndCaveBiome.create(biomeConfig); if (Configs.BIOME_CONFIG.getBoolean(biome.getID(), "enabled", true)) { BiomeAPI.registerBiome(biome, END_CAVE); + //ALL_BE_BIOMES.add(biome); } return biome; }