mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-02-06 18:34:59 +01:00
Sync AllPortalTracks with Create Fabric (#7078)
Co-authored-by: butterflysky <bright.art1794@fastmail.com>
This commit is contained in:
parent
4abc72a5a2
commit
bdc0e8779d
3 changed files with 208 additions and 32 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue