Immersive Portal Accessor refactoring
This commit is contained in:
+212
-99
@@ -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
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user