From 0d013c88063f132db7eb80538a579c58f47a0c1f Mon Sep 17 00:00:00 2001 From: TropheusJ Date: Wed, 19 Feb 2025 12:40:55 -0500 Subject: [PATCH] woe, API status be upon ye - make portal track behavior API - some cleanup while I'm at it --- .../train/PortalTrackProvider.java | 91 +++++++++++++ .../content/trains/track/AllPortalTracks.java | 120 +++--------------- .../content/trains/track/TrackBlock.java | 12 +- 3 files changed, 116 insertions(+), 107 deletions(-) create mode 100644 src/main/java/com/simibubi/create/api/contraption/train/PortalTrackProvider.java diff --git a/src/main/java/com/simibubi/create/api/contraption/train/PortalTrackProvider.java b/src/main/java/com/simibubi/create/api/contraption/train/PortalTrackProvider.java new file mode 100644 index 0000000000..39b2beadd5 --- /dev/null +++ b/src/main/java/com/simibubi/create/api/contraption/train/PortalTrackProvider.java @@ -0,0 +1,91 @@ +package com.simibubi.create.api.contraption.train; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import org.jetbrains.annotations.Nullable; + +import com.simibubi.create.api.registry.SimpleRegistry; +import com.simibubi.create.content.contraptions.glue.SuperGlueEntity; +import com.simibubi.create.content.trains.track.AllPortalTracks; + +import net.createmod.catnip.math.BlockFace; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.portal.PortalInfo; + +import net.minecraftforge.common.util.ITeleporter; + +/** + * A provider for portal track connections. + * Takes a track inbound through a portal and finds the exit location for the outbound track. + */ +@FunctionalInterface +public interface PortalTrackProvider { + SimpleRegistry REGISTRY = SimpleRegistry.create(); + + /** + * Find the exit location for a track going through a portal. + * @param level the level of the inbound track + * @param face the face of the inbound track + */ + Exit findExit(ServerLevel level, BlockFace face); + + /** + * Checks if a given {@link BlockState} represents a supported portal block. + * @param state The block state to check. + * @return {@code true} if the block state represents a supported portal; {@code false} otherwise. + */ + static boolean isSupportedPortal(BlockState state) { + return REGISTRY.get(state) != null; + } + + /** + * Retrieves the corresponding outbound track on the other side of a portal. + * @param level The current {@link ServerLevel}. + * @param inboundTrack The inbound track {@link BlockFace}. + * @return the found outbound track, or null if one wasn't found. + */ + @Nullable + static Exit getOtherSide(ServerLevel level, BlockFace inboundTrack) { + BlockPos portalPos = inboundTrack.getConnectedPos(); + BlockState portalState = level.getBlockState(portalPos); + PortalTrackProvider provider = REGISTRY.get(portalState); + return provider == null ? null : provider.findExit(level, inboundTrack); + } + + /** + * Find an exit location by using an {@link ITeleporter} instance. + * @param level The level of the inbound track + * @param face The face of the inbound track + * @param firstDimension The first dimension (typically the Overworld) + * @param secondDimension The second dimension (e.g., Nether, Aether) + * @param customPortalForcer A function to obtain the {@link ITeleporter} for the target level + * @return A found exit, or null if one wasn't found + */ + static Exit fromTeleporter(ServerLevel level, BlockFace face, ResourceKey firstDimension, + ResourceKey secondDimension, Function customPortalForcer) { + return AllPortalTracks.fromTeleporter(level, face, firstDimension, secondDimension, customPortalForcer); + } + + /** + * Find an exit location by teleporting a probe entity to find a {@link PortalInfo}. + * @param level The level of the inbound track + * @param face The face of the inbound track + * @param firstDimension The first dimension + * @param secondDimension The second dimension + * @param portalInfoProvider A function that provides the {@link PortalInfo} given the target level and probe entity. + * @return A found exit, or null if one wasn't found + */ + static Exit fromProbe(ServerLevel level, BlockFace face, ResourceKey firstDimension, + ResourceKey secondDimension, BiFunction portalInfoProvider) { + return AllPortalTracks.fromProbe(level, face, firstDimension, secondDimension, portalInfoProvider); + } + + record Exit(ServerLevel level, BlockFace face) { + } +} diff --git a/src/main/java/com/simibubi/create/content/trains/track/AllPortalTracks.java b/src/main/java/com/simibubi/create/content/trains/track/AllPortalTracks.java index 125d11bdaa..b128bd4104 100644 --- a/src/main/java/com/simibubi/create/content/trains/track/AllPortalTracks.java +++ b/src/main/java/com/simibubi/create/content/trains/track/AllPortalTracks.java @@ -2,15 +2,13 @@ package com.simibubi.create.content.trains.track; import java.util.function.BiFunction; import java.util.function.Function; -import java.util.function.UnaryOperator; import com.simibubi.create.Create; -import com.simibubi.create.api.registry.SimpleRegistry; +import com.simibubi.create.api.contraption.train.PortalTrackProvider; import com.simibubi.create.compat.Mods; import com.simibubi.create.compat.betterend.BetterEndPortalCompat; import com.simibubi.create.content.contraptions.glue.SuperGlueEntity; -import net.createmod.catnip.data.Pair; import net.createmod.catnip.math.BlockFace; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -38,19 +36,6 @@ import net.minecraftforge.registries.ForgeRegistries; *

*/ public class AllPortalTracks { - /** - * Functional interface representing a provider for portal track connections. - * It takes a pair of {@link ServerLevel} and {@link BlockFace} representing the inbound track - * and returns a similar pair for the outbound track. - */ - @FunctionalInterface - public interface PortalTrackProvider extends UnaryOperator> {} - - /** - * Registry mapping portal blocks to their respective {@link PortalTrackProvider}s. - */ - public static final SimpleRegistry REGISTRY = SimpleRegistry.create(); - /** * Registers a portal track integration for a given block identified by its {@link ResourceLocation}, if it exists. * If it does not, a warning will be logged. @@ -61,37 +46,12 @@ public class AllPortalTracks { public static void tryRegisterIntegration(ResourceLocation id, PortalTrackProvider provider) { if (ForgeRegistries.BLOCKS.containsKey(id)) { Block block = ForgeRegistries.BLOCKS.getValue(id); - REGISTRY.register(block, provider); + PortalTrackProvider.REGISTRY.register(block, provider); } else { Create.LOGGER.warn("Portal for integration wasn't found: {}. Compat outdated?", id); } } - /** - * Checks if a given {@link BlockState} represents a supported portal block. - * - * @param state The block state to check. - * @return {@code true} if the block state represents a supported portal; {@code false} otherwise. - */ - public static boolean isSupportedPortal(BlockState state) { - return REGISTRY.get(state.getBlock()) != null; - } - - /** - * Retrieves the corresponding outbound track on the other side of a portal. - * - * @param level The current {@link ServerLevel}. - * @param inboundTrack The inbound track {@link BlockFace}. - * @return A pair containing the target {@link ServerLevel} and outbound {@link BlockFace}, - * or {@code null} if no corresponding portal is found. - */ - public static Pair getOtherSide(ServerLevel level, BlockFace inboundTrack) { - BlockPos portalPos = inboundTrack.getConnectedPos(); - BlockState portalState = level.getBlockState(portalPos); - PortalTrackProvider provider = REGISTRY.get(portalState.getBlock()); - return provider == null ? null : provider.apply(Pair.of(level, inboundTrack)); - } - // Built-in handlers /** @@ -99,7 +59,7 @@ public class AllPortalTracks { * This includes the Nether and the Aether (if loaded). */ public static void registerDefaults() { - REGISTRY.register(Blocks.NETHER_PORTAL, AllPortalTracks::nether); + PortalTrackProvider.REGISTRY.register(Blocks.NETHER_PORTAL, AllPortalTracks::nether); if (Mods.AETHER.isLoaded()) { tryRegisterIntegration(Mods.AETHER.rl("aether_portal"), AllPortalTracks::aether); @@ -110,72 +70,41 @@ public class AllPortalTracks { } } - /** - * Portal track provider for the Nether portal. - * - * @param inbound A pair containing the current {@link ServerLevel} and inbound {@link BlockFace}. - * @return A pair with the target {@link ServerLevel} and outbound {@link BlockFace}, or {@code null} if not applicable. - */ - private static Pair nether(Pair inbound) { - ServerLevel level = inbound.getFirst(); + private static PortalTrackProvider.Exit nether(ServerLevel level, BlockFace face) { MinecraftServer minecraftServer = level.getServer(); if (!minecraftServer.isNetherEnabled()) return null; - return standardPortalProvider(inbound, Level.OVERWORLD, Level.NETHER, ServerLevel::getPortalForcer); + return PortalTrackProvider.fromTeleporter(level, face, Level.OVERWORLD, Level.NETHER, ServerLevel::getPortalForcer); } - /** - * Portal track provider for the Aether mod's portal. - * - * @param inbound A pair containing the current {@link ServerLevel} and inbound {@link BlockFace}. - * @return A pair with the target {@link ServerLevel} and outbound {@link BlockFace}, or {@code null} if not applicable. - */ - private static Pair aether(Pair inbound) { - ResourceKey aetherLevelKey = - ResourceKey.create(Registries.DIMENSION, Mods.AETHER.rl("the_aether")); - return standardPortalProvider(inbound, Level.OVERWORLD, aetherLevelKey, level -> { + private static PortalTrackProvider.Exit aether(ServerLevel level, BlockFace face) { + ResourceKey aetherLevelKey = ResourceKey.create(Registries.DIMENSION, Mods.AETHER.rl("the_aether")); + return PortalTrackProvider.fromTeleporter(level, face, Level.OVERWORLD, aetherLevelKey, serverLevel -> { try { return (ITeleporter) Class.forName("com.aetherteam.aether.block.portal.AetherPortalForcer") .getDeclaredConstructor(ServerLevel.class, boolean.class) - .newInstance(level, true); + .newInstance(serverLevel, true); } catch (Exception e) { Create.LOGGER.error("Failed to create Aether teleporter: ", e); } - return level.getPortalForcer(); + return serverLevel.getPortalForcer(); }); } - /** - * Portal track provider for the Better End mod's portal. - * - * @param inbound A pair containing the current {@link ServerLevel} and inbound {@link BlockFace}. - * @return A pair with the target {@link ServerLevel} and outbound {@link BlockFace}, or {@code null} if not applicable. - */ - private static Pair betterend(Pair inbound) { - return portalProvider(inbound, Level.OVERWORLD, Level.END, BetterEndPortalCompat::getBetterEndPortalInfo); + private static PortalTrackProvider.Exit betterend(ServerLevel level, BlockFace face) { + return fromProbe(level, face, Level.OVERWORLD, Level.END, BetterEndPortalCompat::getBetterEndPortalInfo); } - /** - * Provides a standard portal track provider that handles portal traversal between two dimensions. - * - * @param inbound A pair containing the current {@link ServerLevel} and inbound {@link BlockFace}. - * @param firstDimension The first dimension (typically the Overworld). - * @param secondDimension The second dimension (e.g., Nether, Aether). - * @param customPortalForcer A function to obtain the {@link ITeleporter} for the target level. - * @return A pair with the target {@link ServerLevel} and outbound {@link BlockFace}, or {@code null} if not applicable. - */ - public static Pair standardPortalProvider( - Pair inbound, + public static PortalTrackProvider.Exit fromTeleporter( + ServerLevel level, BlockFace inboundTrack, ResourceKey firstDimension, ResourceKey secondDimension, Function customPortalForcer ) { - return portalProvider( - inbound, - firstDimension, - secondDimension, + return PortalTrackProvider.fromProbe( + level, inboundTrack, firstDimension, secondDimension, (otherLevel, probe) -> { ITeleporter teleporter = customPortalForcer.apply(otherLevel); return teleporter.getPortalInfo(probe, otherLevel, probe::findDimensionEntryPoint); @@ -183,22 +112,12 @@ public class AllPortalTracks { ); } - /** - * Generalized portal provider method that calculates the corresponding outbound track across a portal. - * - * @param inbound A pair containing the current {@link ServerLevel} and inbound {@link BlockFace}. - * @param firstDimension The first dimension. - * @param secondDimension The second dimension. - * @param portalInfoProvider A function that provides the {@link PortalInfo} given the target level and probe entity. - * @return A pair with the target {@link ServerLevel} and outbound {@link BlockFace}, or {@code null} if not applicable. - */ - public static Pair portalProvider( - Pair inbound, + public static PortalTrackProvider.Exit fromProbe( + ServerLevel level, BlockFace inboundTrack, ResourceKey firstDimension, ResourceKey secondDimension, BiFunction portalInfoProvider ) { - ServerLevel level = inbound.getFirst(); ResourceKey resourceKey = level.dimension() == secondDimension ? firstDimension : secondDimension; MinecraftServer minecraftServer = level.getServer(); @@ -207,7 +126,6 @@ public class AllPortalTracks { if (otherLevel == null) return null; - BlockFace inboundTrack = inbound.getSecond(); BlockPos portalPos = inboundTrack.getConnectedPos(); BlockState portalState = level.getBlockState(portalPos); @@ -228,6 +146,6 @@ public class AllPortalTracks { if (targetDirection.getAxis() == otherPortalState.getValue(BlockStateProperties.HORIZONTAL_AXIS)) targetDirection = targetDirection.getClockWise(); BlockPos otherPos = otherPortalPos.relative(targetDirection); - return Pair.of(otherLevel, new BlockFace(otherPos, targetDirection.getOpposite())); + return new PortalTrackProvider.Exit(otherLevel, new BlockFace(otherPos, targetDirection.getOpposite())); } } diff --git a/src/main/java/com/simibubi/create/content/trains/track/TrackBlock.java b/src/main/java/com/simibubi/create/content/trains/track/TrackBlock.java index 6003bd5875..281d993625 100644 --- a/src/main/java/com/simibubi/create/content/trains/track/TrackBlock.java +++ b/src/main/java/com/simibubi/create/content/trains/track/TrackBlock.java @@ -28,6 +28,7 @@ import com.simibubi.create.AllBlocks; import com.simibubi.create.AllPartialModels; import com.simibubi.create.AllShapes; import com.simibubi.create.AllTags; +import com.simibubi.create.api.contraption.train.PortalTrackProvider; import com.simibubi.create.api.schematic.requirement.SpecialBlockItemRequirement; import com.simibubi.create.content.decoration.girder.GirderBlock; import com.simibubi.create.content.equipment.wrench.IWrenchable; @@ -50,7 +51,6 @@ import dev.engine_room.flywheel.lib.transform.TransformStack; import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.createmod.catnip.data.Iterate; -import net.createmod.catnip.data.Pair; import net.createmod.catnip.math.AngleHelper; import net.createmod.catnip.math.BlockFace; import net.createmod.catnip.math.VecHelper; @@ -253,18 +253,18 @@ public class TrackBlock extends Block for (Direction d : Iterate.directionsInAxis(portalTest)) { BlockPos portalPos = pos.relative(d); BlockState portalState = level.getBlockState(portalPos); - if (!AllPortalTracks.isSupportedPortal(portalState)) + if (!PortalTrackProvider.isSupportedPortal(portalState)) continue; pop = true; - Pair otherSide = AllPortalTracks.getOtherSide(level, new BlockFace(pos, d)); + PortalTrackProvider.Exit otherSide = PortalTrackProvider.getOtherSide(level, new BlockFace(pos, d)); if (otherSide == null) { fail = "missing"; continue; } - ServerLevel otherLevel = otherSide.getFirst(); - BlockFace otherTrack = otherSide.getSecond(); + ServerLevel otherLevel = otherSide.level(); + BlockFace otherTrack = otherSide.face(); BlockPos otherTrackPos = otherTrack.getPos(); BlockState existing = otherLevel.getBlockState(otherTrackPos); if (!existing.canBeReplaced()) { @@ -325,7 +325,7 @@ public class TrackBlock extends Block BlockPos portalPos = pCurrentPos.relative(d); BlockState portalState = level.getBlockState(portalPos); - if (!AllPortalTracks.isSupportedPortal(portalState)) + if (!PortalTrackProvider.isSupportedPortal(portalState)) return Blocks.AIR.defaultBlockState(); }