From bdc0e8779da666b43d9dff11729721635f3f4b9e Mon Sep 17 00:00:00 2001 From: IThundxr Date: Fri, 10 Jan 2025 12:02:50 -0500 Subject: [PATCH] Sync AllPortalTracks with Create Fabric (#7078) Co-authored-by: butterflysky --- .../java/com/simibubi/create/compat/Mods.java | 7 +- .../betterend/BetterEndPortalCompat.java | 66 +++++++ .../content/trains/track/AllPortalTracks.java | 167 +++++++++++++++--- 3 files changed, 208 insertions(+), 32 deletions(-) create mode 100644 src/main/java/com/simibubi/create/compat/betterend/BetterEndPortalCompat.java diff --git a/src/main/java/com/simibubi/create/compat/Mods.java b/src/main/java/com/simibubi/create/compat/Mods.java index a78d2b780..df64a92df 100644 --- a/src/main/java/com/simibubi/create/compat/Mods.java +++ b/src/main/java/com/simibubi/create/compat/Mods.java @@ -33,7 +33,8 @@ public enum Mods { XLPACKETS, MODERNUI, FTBCHUNKS, - JOURNEYMAP; + JOURNEYMAP, + BETTEREND; private final String id; @@ -55,11 +56,11 @@ public enum Mods { public Block getBlock(String id) { return ForgeRegistries.BLOCKS.getValue(rl(id)); } - + public Item getItem(String id) { return ForgeRegistries.ITEMS.getValue(rl(id)); } - + public boolean contains(ItemLike entry) { if (!isLoaded()) return false; diff --git a/src/main/java/com/simibubi/create/compat/betterend/BetterEndPortalCompat.java b/src/main/java/com/simibubi/create/compat/betterend/BetterEndPortalCompat.java new file mode 100644 index 000000000..3387d2e12 --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/betterend/BetterEndPortalCompat.java @@ -0,0 +1,66 @@ +package com.simibubi.create.compat.betterend; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; + +import com.simibubi.create.Create; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.portal.PortalInfo; + +public class BetterEndPortalCompat { + private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); + + private static MethodHandle constructorHandle; + private static VarHandle portalEntrancePosHandle; + private static MethodHandle findDimensionEntryPointHandle; + + private static boolean hasErrored = false; + + static { + try { + Class travelerStateClass = Class.forName("org.betterx.betterend.portal.TravelerState"); + MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(travelerStateClass, lookup); + + MethodType travelerStateConstructorTypes = MethodType.methodType(void.class, Entity.class); + constructorHandle = lookup.findConstructor(travelerStateClass, travelerStateConstructorTypes); + + portalEntrancePosHandle = privateLookup.findVarHandle(travelerStateClass, "portalEntrancePos", BlockPos.class); + + MethodType findDimensionEntryPointTypes = MethodType.methodType(PortalInfo.class, ServerLevel.class); + findDimensionEntryPointHandle = privateLookup.findVirtual(travelerStateClass, "findDimensionEntryPoint", findDimensionEntryPointTypes); + } catch (Exception e) { + Create.LOGGER.error("Create's Better End Portal compat failed to initialize: ", e); + hasErrored = true; + } + } + + /** + * Retrieves the adjusted {@link PortalInfo} for the Better End portal using reflection. + * + * @param targetLevel The target {@link ServerLevel} (dimension). + * @param entity The probe {@link Entity} used for portal traversal calculations. + * @return The adjusted {@link PortalInfo} for the target dimension, or {@code null} if an error occurs. + */ + public static PortalInfo getBetterEndPortalInfo(ServerLevel targetLevel, Entity entity) { + if (!hasErrored) { + try { + Object travelerState = constructorHandle.invoke(entity); + + // Set the private portalEntrancePos field to the entity's block position + // as assumed in TravelerState#findDimensionEntryPoint + portalEntrancePosHandle.set(travelerState, entity.blockPosition().immutable()); + + return (PortalInfo) findDimensionEntryPointHandle.invoke(travelerState, targetLevel); + } catch (Throwable e) { + Create.LOGGER.error("Create's Better End Portal compat failed to initialize: ", e); + } + } + + return null; + } +} 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 f74dece9a..4fb8b200c 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 @@ -1,9 +1,12 @@ 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.compat.Mods; +import com.simibubi.create.compat.betterend.BetterEndPortalCompat; import com.simibubi.create.content.contraptions.glue.SuperGlueEntity; import com.simibubi.create.foundation.utility.AttachedRegistry; import com.simibubi.create.foundation.utility.BlockFace; @@ -26,30 +29,66 @@ import net.minecraft.world.phys.AABB; import net.minecraftforge.common.util.ITeleporter; import net.minecraftforge.registries.ForgeRegistries; +/** + * Manages portal track integrations for various dimensions and mods within the Create mod. + *

+ * Portals must be entered from the side and must lead to a different dimension than the one entered from. + * This class handles the registration and functionality of portal tracks for standard and modded portals. + *

+ */ public class AllPortalTracks { - - // Portals must be entered from the side and must lead to a different dimension - // than the one entered from - + /** + * 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> { - }; + public interface PortalTrackProvider extends UnaryOperator> {} + /** + * Registry mapping portal blocks to their respective {@link PortalTrackProvider}s. + */ private static final AttachedRegistry PORTAL_BEHAVIOURS = - new AttachedRegistry<>(ForgeRegistries.BLOCKS); + new AttachedRegistry<>(ForgeRegistries.BLOCKS); + /** + * Registers a portal track integration for a given block identified by its {@link ResourceLocation}. + * + * @param block The resource location of the portal block. + * @param provider The portal track provider for the block. + */ public static void registerIntegration(ResourceLocation block, PortalTrackProvider provider) { PORTAL_BEHAVIOURS.register(block, provider); } + /** + * Registers a portal track integration for a given {@link Block}. + * + * @param block The portal block. + * @param provider The portal track provider for the block. + */ public static void registerIntegration(Block block, PortalTrackProvider provider) { PORTAL_BEHAVIOURS.register(block, provider); } + /** + * 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 PORTAL_BEHAVIOURS.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); @@ -57,61 +96,132 @@ public class AllPortalTracks { return provider == null ? null : provider.apply(Pair.of(level, inboundTrack)); } - // Builtin handlers + // Built-in handlers + /** + * Registers default portal track integrations for built-in dimensions and mods. + * This includes the Nether and the Aether (if loaded). + */ public static void registerDefaults() { registerIntegration(Blocks.NETHER_PORTAL, AllPortalTracks::nether); if (Mods.AETHER.isLoaded()) - registerIntegration(new ResourceLocation("aether", "aether_portal"), AllPortalTracks::aether); + registerIntegration(Mods.AETHER.rl("aether_portal"), AllPortalTracks::aether); + if (Mods.BETTEREND.isLoaded()) + registerIntegration(Mods.BETTEREND.rl("end_portal_block"), AllPortalTracks::betterend); } + /** + * 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(); + MinecraftServer minecraftServer = level.getServer(); + + if (!minecraftServer.isNetherEnabled()) + return null; + return standardPortalProvider(inbound, 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, new ResourceLocation("aether", "the_aether")); + ResourceKey.create(Registries.DIMENSION, Mods.AETHER.rl("the_aether")); return standardPortalProvider(inbound, Level.OVERWORLD, aetherLevelKey, level -> { try { return (ITeleporter) Class.forName("com.aetherteam.aether.block.portal.AetherPortalForcer") - .getDeclaredConstructor(ServerLevel.class, boolean.class) - .newInstance(level, true); + .getDeclaredConstructor(ServerLevel.class, boolean.class) + .newInstance(level, true); } catch (Exception e) { - e.printStackTrace(); + Create.LOGGER.error("Failed to create Aether teleporter: ", e); } return level.getPortalForcer(); }); } - public static Pair standardPortalProvider(Pair inbound, - ResourceKey firstDimension, ResourceKey secondDimension, - Function customPortalForcer) { - ServerLevel level = inbound.getFirst(); - ResourceKey resourcekey = level.dimension() == secondDimension ? firstDimension : secondDimension; - MinecraftServer minecraftserver = level.getServer(); - ServerLevel otherLevel = minecraftserver.getLevel(resourcekey); + /** + * 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); + } - if (otherLevel == null || !minecraftserver.isNetherEnabled()) + /** + * 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, + ResourceKey firstDimension, + ResourceKey secondDimension, + Function customPortalForcer + ) { + return portalProvider( + inbound, + firstDimension, + secondDimension, + (otherLevel, probe) -> { + ITeleporter teleporter = customPortalForcer.apply(otherLevel); + return teleporter.getPortalInfo(probe, otherLevel, probe::findDimensionEntryPoint); + } + ); + } + + /** + * 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, + ResourceKey firstDimension, + ResourceKey secondDimension, + BiFunction portalInfoProvider + ) { + ServerLevel level = inbound.getFirst(); + ResourceKey resourceKey = level.dimension() == secondDimension ? firstDimension : secondDimension; + + MinecraftServer minecraftServer = level.getServer(); + ServerLevel otherLevel = minecraftServer.getLevel(resourceKey); + + if (otherLevel == null) return null; BlockFace inboundTrack = inbound.getSecond(); BlockPos portalPos = inboundTrack.getConnectedPos(); BlockState portalState = level.getBlockState(portalPos); - ITeleporter teleporter = customPortalForcer.apply(otherLevel); SuperGlueEntity probe = new SuperGlueEntity(level, new AABB(portalPos)); - probe.setYRot(inboundTrack.getFace() - .toYRot()); + probe.setYRot(inboundTrack.getFace().toYRot()); probe.setPortalEntrancePos(); - PortalInfo portalinfo = teleporter.getPortalInfo(probe, otherLevel, probe::findDimensionEntryPoint); - if (portalinfo == null) + PortalInfo portalInfo = portalInfoProvider.apply(otherLevel, probe); + if (portalInfo == null) return null; - BlockPos otherPortalPos = BlockPos.containing(portalinfo.pos); + BlockPos otherPortalPos = BlockPos.containing(portalInfo.pos); BlockState otherPortalState = otherLevel.getBlockState(otherPortalPos); - if (otherPortalState.getBlock() != portalState.getBlock()) + if (!otherPortalState.is(portalState.getBlock())) return null; Direction targetDirection = inboundTrack.getFace(); @@ -120,5 +230,4 @@ public class AllPortalTracks { BlockPos otherPos = otherPortalPos.relative(targetDirection); return Pair.of(otherLevel, new BlockFace(otherPos, targetDirection.getOpposite())); } - }