Immersive Portal Accessor refactoring

This commit is contained in:
James Seibel
2026-05-02 09:55:00 -05:00
parent 4e647395e8
commit 52b0acc452
@@ -23,7 +23,9 @@ import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import org.jetbrains.annotations.NotNull;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -35,27 +37,53 @@ import java.util.function.Supplier;
public abstract class ImmersivePortalsAbstractAccessor implements IImmersivePortalsAccessor
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static Class<?> portal;
private static MethodHandle isRendering;
private static Method shouldSkipRenderingPortal;
private static MethodHandle getGlobalPortals;
private static Class<?> portalClass;
private static MethodHandle isRenderingMethodHandle;
private static Method shouldSkipRenderingPortalMethod;
private static MethodHandle getGlobalPortalsMethodHandle;
private static long lastPortalTime = -1;
private static long lastPortalMsTime = -1;
private static boolean portalVisible = false;
public ImmersivePortalsAbstractAccessor() {
DhApi.events.bind(DhApiBeforeRenderEvent.class, BeforeRender.INSTANCE);
//=============//
// constructor //
//=============//
//region
public ImmersivePortalsAbstractAccessor()
{
LOGGER.info("Immersive Portals detected: some DH features will be disabled or may only partially function.");
BeforeRenderEvent event = new BeforeRenderEvent(this);
DhApi.events.bind(DhApiBeforeRenderEvent.class, event);
}
private static Class<?> getPortalRenderingClass() {
try {
//endregion
//=====================//
// reflection handling //
//=====================//
//region
private static Class<?> getPortalRenderingClass()
{
try
{
return Class.forName("qouteall.imm_ptl.core.render.context_management.PortalRendering");
} catch (ClassNotFoundException first) {
try {
}
catch (ClassNotFoundException first)
{
try
{
return Class.forName("com.qouteall.immersive_portals.render.context_management.PortalRendering"); // 1.16
} catch (ClassNotFoundException second) {
}
catch (ClassNotFoundException second)
{
RuntimeException err = new RuntimeException(first);
err.addSuppressed(second);
throw err;
@@ -63,16 +91,26 @@ public abstract class ImmersivePortalsAbstractAccessor implements IImmersivePort
}
}
private static Class<?> getPortalRendererClass() {
try {
private static Class<?> getPortalRendererClass()
{
try
{
return Class.forName("qouteall.imm_ptl.core.render.renderer.PortalRenderer"); // 1.21+
} catch (ClassNotFoundException first) {
try {
}
catch (ClassNotFoundException first)
{
try
{
return Class.forName("qouteall.imm_ptl.core.render.PortalRenderer");
} catch (ClassNotFoundException second) {
try {
}
catch (ClassNotFoundException second)
{
try
{
return Class.forName("com.qouteall.immersive_portals.render.PortalRenderer"); // 1.16
} catch (ClassNotFoundException third) {
}
catch (ClassNotFoundException third)
{
RuntimeException err = new RuntimeException(first);
err.addSuppressed(second);
err.addSuppressed(third);
@@ -82,28 +120,42 @@ public abstract class ImmersivePortalsAbstractAccessor implements IImmersivePort
}
}
private static Class<?> getPortalClass() {
try {
portal = Class.forName("qouteall.imm_ptl.core.portal.Portal");
} catch (ClassNotFoundException first) {
try {
portal = Class.forName("com.qouteall.immersive_portals.portal.Portal"); // 1.16
} catch (ClassNotFoundException second) {
private static Class<?> getPortalClass()
{
try
{
portalClass = Class.forName("qouteall.imm_ptl.core.portal.Portal");
}
catch (ClassNotFoundException first)
{
try
{
portalClass = Class.forName("com.qouteall.immersive_portals.portal.Portal"); // 1.16
}
catch (ClassNotFoundException second)
{
RuntimeException err = new RuntimeException(first);
err.addSuppressed(second);
throw err;
}
}
return portal;
return portalClass;
}
private static Class<?> getGlobalPortalStorageClass() {
try {
private static Class<?> getGlobalPortalStorageClass()
{
try
{
return Class.forName("qouteall.imm_ptl.core.portal.global_portals.GlobalPortalStorage");
} catch (ClassNotFoundException first) {
try {
}
catch (ClassNotFoundException first)
{
try
{
return Class.forName("com.qouteall.immersive_portals.McHelper"); // 1.16
} catch (ClassNotFoundException second) {
}
catch (ClassNotFoundException second)
{
RuntimeException err = new RuntimeException(first);
err.addSuppressed(second);
throw err;
@@ -111,13 +163,20 @@ public abstract class ImmersivePortalsAbstractAccessor implements IImmersivePort
}
}
private static Class<?> getIPCGlobalClass() {
try {
private static Class<?> getIPCGlobalClass()
{
try
{
return Class.forName("qouteall.imm_ptl.core.IPCGlobal");
} catch (ClassNotFoundException first) {
try {
}
catch (ClassNotFoundException first)
{
try
{
return Class.forName("com.qouteall.immersive_portals.CGlobal"); // 1.16
} catch (ClassNotFoundException second) {
}
catch (ClassNotFoundException second)
{
RuntimeException err = new RuntimeException(first);
err.addSuppressed(second);
throw err;
@@ -125,22 +184,30 @@ public abstract class ImmersivePortalsAbstractAccessor implements IImmersivePort
}
}
private static boolean shouldSkipRenderingPortal(Object portal, Supplier<?> frustumSupplier) {
try {
if (shouldSkipRenderingPortal == null) {
shouldSkipRenderingPortal = getPortalRendererClass().getDeclaredMethod(
private static boolean shouldSkipRenderingPortal(Object portal, Supplier<?> frustumSupplier)
{
try
{
if (shouldSkipRenderingPortalMethod == null)
{
shouldSkipRenderingPortalMethod = getPortalRendererClass().getDeclaredMethod(
"shouldSkipRenderingPortal", getPortalClass(), Supplier.class
);
shouldSkipRenderingPortal.setAccessible(true);
shouldSkipRenderingPortalMethod.setAccessible(true);
}
if (Modifier.isStatic(shouldSkipRenderingPortal.getModifiers())) {
return (boolean) shouldSkipRenderingPortal.invoke(null, portal, frustumSupplier);
} else {
return (boolean) shouldSkipRenderingPortal.invoke(
if (Modifier.isStatic(shouldSkipRenderingPortalMethod.getModifiers()))
{
return (boolean) shouldSkipRenderingPortalMethod.invoke(null, portal, frustumSupplier);
}
else
{
return (boolean) shouldSkipRenderingPortalMethod.invoke(
getIPCGlobalClass().getField("renderer").get(null), portal, frustumSupplier
);
}
} catch (Throwable e) {
}
catch (Throwable e)
{
throw new RuntimeException(e);
}
}
@@ -153,37 +220,21 @@ public abstract class ImmersivePortalsAbstractAccessor implements IImmersivePort
protected abstract Supplier<?> getFrustumSupplier();
private static boolean isPortal(Object object) {
return getPortalClass().isInstance(object);
}
private List<?> getGlobalPortals(Object level) {
try {
if (getGlobalPortals == null) {
getGlobalPortals = MethodHandles.lookup().findStatic(
private List<?> getGlobalPortals(Object level)
{
try
{
if (getGlobalPortalsMethodHandle == null)
{
getGlobalPortalsMethodHandle = MethodHandles.lookup().findStatic(
getGlobalPortalStorageClass(),
"getGlobalPortals", MethodType.methodType(List.class).appendParameterTypes(
getLevelClass()
)
);
}
return (List<?>) getGlobalPortals.invoke(level);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public boolean isRenderingPortal()
{
try {
if (isRendering == null) {
isRendering = MethodHandles.lookup().findStatic(
getPortalRenderingClass(),
"isRendering", MethodType.methodType(Boolean.TYPE)
);
}
return (boolean) isRendering.invoke();
return (List<?>) getGlobalPortalsMethodHandle.invoke(level);
}
catch (Throwable e)
{
@@ -191,66 +242,128 @@ public abstract class ImmersivePortalsAbstractAccessor implements IImmersivePort
}
}
/**
* Essentially reimplements PortalRenderer::getPortalsToRender because it did not exist in 1.16.
*/
private boolean isPortalVisibleRightNow() {
Supplier<?> frustumSupplier = getFrustumSupplier();
if (frustumSupplier == null) return false;
for (Object portal : getGlobalPortals(getClientLevel())) {
if (!shouldSkipRenderingPortal(portal, frustumSupplier)) {
return true;
//endregion
//===========//
// overrides //
//===========//
//region
@Override
public boolean isRenderingPortal()
{
try
{
if (isRenderingMethodHandle == null)
{
isRenderingMethodHandle = MethodHandles.lookup().findStatic(
getPortalRenderingClass(),
"isRendering", MethodType.methodType(Boolean.TYPE)
);
}
return (boolean) isRenderingMethodHandle.invoke();
}
for (Object entity : getEntitiesForRendering()) {
if (isPortal(entity) && !shouldSkipRenderingPortal(entity, frustumSupplier)) {
return true;
}
catch (Throwable e)
{
throw new RuntimeException(e);
}
return false;
}
@Override
public boolean wasPortalRecentlyVisible() {
public boolean wasPortalRecentlyVisible()
{
// I did consider setting portalVisible to true whenever PortalRendering::isRendering was true instead,
// but that would still render fading immediately after startup before entering the portal at least once.
// This is more robust, but slightly worse for performance. Still, people can just turn fading off if they have issues.
boolean isPortalVisible = isPortalVisibleRightNow();
if (isPortalVisible) {
lastPortalTime = System.currentTimeMillis();
if (isPortalVisible)
{
lastPortalMsTime = System.currentTimeMillis();
portalVisible = true;
} else if (portalVisible) {
if (System.currentTimeMillis() - lastPortalTime > 1000) {
}
else if (portalVisible)
{
if (System.currentTimeMillis() - lastPortalMsTime > 1000)
{
portalVisible = false;
}
}
// Simply checking portal visibility right now is not sufficient, that will still render the fading on top of the portal.
// Instead, we check if a portal was rendered during the last second or so.
return portalVisible;
}
@Override
public String getModName()
/** Essentially reimplements PortalRenderer::getPortalsToRender because it did not exist in 1.16. */
private boolean isPortalVisibleRightNow()
{
return "ImmersivePortalsMod";
Supplier<?> frustumSupplier = getFrustumSupplier();
if (frustumSupplier == null)
{
return false;
}
for (Object portal : getGlobalPortals(getClientLevel()))
{
if (!shouldSkipRenderingPortal(portal, frustumSupplier))
{
return true;
}
}
for (Object entity : getEntitiesForRendering())
{
if (isPortalObject(entity)
&& !shouldSkipRenderingPortal(entity, frustumSupplier))
{
return true;
}
}
return false;
}
private static boolean isPortalObject(Object object) { return getPortalClass().isInstance(object); }
public static class BeforeRender extends DhApiBeforeRenderEvent
//endregion
@Override
public String getModName() { return "Immersive Portals"; }
//=======//
// event //
//=======//
//region
private static class BeforeRenderEvent extends DhApiBeforeRenderEvent
{
@NotNull
private final IImmersivePortalsAccessor immersivePortals;
public static BeforeRender INSTANCE = new BeforeRender();
private BeforeRender() {}
public BeforeRenderEvent(@NotNull IImmersivePortalsAccessor portalAccessor) { this.immersivePortals = portalAccessor; }
@Override
public void beforeRender(DhApiCancelableEventParam<DhApiRenderParam> event)
{
if (ModAccessorInjector.INSTANCE.get(IImmersivePortalsAccessor.class).isRenderingPortal()) {
// needed because otherwise DH doesn't render to the level anyway
// and will probably render the level the player is currently in instead
if (this.immersivePortals.isRenderingPortal())
{
event.cancelEvent();
}
}
}
//endregion
}