Sync AllPortalTracks with Create Fabric (#7078)

Co-authored-by: butterflysky <bright.art1794@fastmail.com>
This commit is contained in:
IThundxr 2025-01-10 12:02:50 -05:00 committed by GitHub
parent 4abc72a5a2
commit bdc0e8779d
Failed to generate hash of commit
3 changed files with 208 additions and 32 deletions

View file

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

View file

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

View file

@ -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.
* <p>
* 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.
* </p>
*/
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<Pair<ServerLevel, BlockFace>> {
};
public interface PortalTrackProvider extends UnaryOperator<Pair<ServerLevel, BlockFace>> {}
/**
* Registry mapping portal blocks to their respective {@link PortalTrackProvider}s.
*/
private static final AttachedRegistry<Block, PortalTrackProvider> 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<ServerLevel, BlockFace> 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<ServerLevel, BlockFace> nether(Pair<ServerLevel, BlockFace> 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<ServerLevel, BlockFace> aether(Pair<ServerLevel, BlockFace> inbound) {
ResourceKey<Level> 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<ServerLevel, BlockFace> standardPortalProvider(Pair<ServerLevel, BlockFace> inbound,
ResourceKey<Level> firstDimension, ResourceKey<Level> secondDimension,
Function<ServerLevel, ITeleporter> customPortalForcer) {
ServerLevel level = inbound.getFirst();
ResourceKey<Level> 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<ServerLevel, BlockFace> betterend(Pair<ServerLevel, BlockFace> 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<ServerLevel, BlockFace> standardPortalProvider(
Pair<ServerLevel, BlockFace> inbound,
ResourceKey<Level> firstDimension,
ResourceKey<Level> secondDimension,
Function<ServerLevel, ITeleporter> 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<ServerLevel, BlockFace> portalProvider(
Pair<ServerLevel, BlockFace> inbound,
ResourceKey<Level> firstDimension,
ResourceKey<Level> secondDimension,
BiFunction<ServerLevel, SuperGlueEntity, PortalInfo> portalInfoProvider
) {
ServerLevel level = inbound.getFirst();
ResourceKey<Level> 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()));
}
}