mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 07:26:48 +01:00
Resolve redirect conflict and add more optifine hacks
- Fix entitiesForRendering redirect to be compatible with carpet. - Use more reflection for dealing with optifine - Fixes issue where flywheel would still be on immediately after enabling optifine shaders
This commit is contained in:
parent
e1af5b2533
commit
a4d3f17fb3
8 changed files with 125 additions and 129 deletions
|
@ -41,8 +41,6 @@ public class Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void refresh() {
|
public static void refresh() {
|
||||||
OptifineHandler.refresh();
|
|
||||||
|
|
||||||
engine = chooseEngine();
|
engine = chooseEngine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,43 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
package com.jozufozu.flywheel.backend;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.minecraft.client.Camera;
|
||||||
|
import net.minecraft.client.renderer.culling.Frustum;
|
||||||
|
|
||||||
public final class OptifineHandler {
|
public final class OptifineHandler {
|
||||||
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
|
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
|
||||||
public static final String SHADER_PACKAGE = "net.optifine.shaders";
|
public static final String SHADER_PACKAGE = "net.optifine.shaders";
|
||||||
|
|
||||||
private static boolean isOptifineInstalled;
|
private static boolean isOptifineInstalled;
|
||||||
private static boolean isUsingShaders;
|
private static BooleanSupplier shadersEnabledSupplier;
|
||||||
private static BooleanSupplier shadowPassSupplier;
|
private static BooleanSupplier shadowPassSupplier;
|
||||||
|
private static FrustumConstructor shadowFrustumConstructor;
|
||||||
|
|
||||||
private OptifineHandler() {
|
private OptifineHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static FrustumConstructor createShadowFrustumConstructor() {
|
||||||
|
try {
|
||||||
|
Class<?> ofShaders = Class.forName("net.optifine.shaders.ShadersRender");
|
||||||
|
Method method = ofShaders.getDeclaredMethod("makeShadowFrustum", Camera.class, Float.TYPE);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return (cam, pt) -> {
|
||||||
|
try {
|
||||||
|
return (Frustum) method.invoke(null, cam, pt);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return ($, $$) -> null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static BooleanSupplier createShadowPassSupplier() {
|
private static BooleanSupplier createShadowPassSupplier() {
|
||||||
try {
|
try {
|
||||||
Class<?> ofShaders = Class.forName("net.optifine.shaders.Shaders");
|
Class<?> ofShaders = Class.forName("net.optifine.shaders.Shaders");
|
||||||
|
@ -37,28 +55,21 @@ public final class OptifineHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean areShadersDisabledInOptifineConfigFile() {
|
private static BooleanSupplier createShadersEnabledSupplier() {
|
||||||
File dir = Minecraft.getInstance().gameDirectory;
|
try {
|
||||||
|
Class<?> ofShaders = Class.forName("net.optifine.shaders.Shaders");
|
||||||
File shaderOptions = new File(dir, "optionsshaders.txt");
|
Field field = ofShaders.getDeclaredField("shaderPackLoaded");
|
||||||
|
field.setAccessible(true);
|
||||||
boolean shadersOff = true;
|
return () -> {
|
||||||
try (BufferedReader reader = new BufferedReader(new FileReader(shaderOptions))) {
|
try {
|
||||||
|
return field.getBoolean(null);
|
||||||
shadersOff = reader.lines()
|
} catch (IllegalAccessException ignored) {
|
||||||
.anyMatch(it -> {
|
return false;
|
||||||
String line = it.replaceAll("\\s", "");
|
}
|
||||||
if (line.startsWith("shaderPack=")) {
|
};
|
||||||
String setting = line.substring("shaderPack=".length());
|
} catch (Exception ignored) {
|
||||||
|
return () -> false;
|
||||||
return setting.equals("OFF") || setting.equals("(internal)");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
} catch (IOException e) {
|
|
||||||
Backend.LOGGER.info("No shader config found.");
|
|
||||||
}
|
}
|
||||||
return shadersOff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
|
@ -67,21 +78,13 @@ public final class OptifineHandler {
|
||||||
|
|
||||||
if (isOptifineInstalled) {
|
if (isOptifineInstalled) {
|
||||||
Backend.LOGGER.info("Optifine detected.");
|
Backend.LOGGER.info("Optifine detected.");
|
||||||
|
|
||||||
refresh();
|
|
||||||
} else {
|
} else {
|
||||||
Backend.LOGGER.info("Optifine not detected.");
|
Backend.LOGGER.info("Optifine not detected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shadersEnabledSupplier = createShadersEnabledSupplier();
|
||||||
shadowPassSupplier = createShadowPassSupplier();
|
shadowPassSupplier = createShadowPassSupplier();
|
||||||
}
|
shadowFrustumConstructor = createShadowFrustumConstructor();
|
||||||
|
|
||||||
public static void refresh() {
|
|
||||||
if (!isOptifineInstalled) return;
|
|
||||||
|
|
||||||
boolean shadersOff = areShadersDisabledInOptifineConfigFile();
|
|
||||||
|
|
||||||
isUsingShaders = !shadersOff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isOptifineInstalled() {
|
public static boolean isOptifineInstalled() {
|
||||||
|
@ -89,10 +92,26 @@ public final class OptifineHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isUsingShaders() {
|
public static boolean isUsingShaders() {
|
||||||
return isUsingShaders;
|
return shadersEnabledSupplier.getAsBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isShadowPass() {
|
public static boolean isShadowPass() {
|
||||||
return shadowPassSupplier.getAsBoolean();
|
return shadowPassSupplier.getAsBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Frustum createShadowFrustum(Camera camera, float partialTicks) {
|
||||||
|
var frustum = shadowFrustumConstructor.create(camera, partialTicks);
|
||||||
|
if (frustum != null) {
|
||||||
|
var position = camera.getPosition();
|
||||||
|
frustum.prepare(position.x, position.y, position.z);
|
||||||
|
}
|
||||||
|
return frustum;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FrustumConstructor {
|
||||||
|
@Nullable
|
||||||
|
Frustum create(Camera camera, float partialTicks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.jozufozu.flywheel.core.Contexts;
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
||||||
import com.jozufozu.flywheel.event.RenderLayerEvent;
|
import com.jozufozu.flywheel.event.RenderLayerEvent;
|
||||||
|
import com.jozufozu.flywheel.util.ClientLevelExtension;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
@ -130,7 +131,8 @@ public class InstanceWorld {
|
||||||
public void loadEntities(ClientLevel world) {
|
public void loadEntities(ClientLevel world) {
|
||||||
// Block entities are loaded while chunks are baked.
|
// Block entities are loaded while chunks are baked.
|
||||||
// Entities are loaded with the world, so when chunks are reloaded they need to be re-added.
|
// Entities are loaded with the world, so when chunks are reloaded they need to be re-added.
|
||||||
world.entitiesForRendering()
|
ClientLevelExtension.cast(world)
|
||||||
|
.flywheel$getAllLoadedEntities()
|
||||||
.forEach(entityInstanceManager::add);
|
.forEach(entityInstanceManager::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
package com.jozufozu.flywheel.mixin;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Group;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.client.renderer.LevelRenderer;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
|
|
||||||
@Mixin(LevelRenderer.class)
|
|
||||||
public class CancelEntityRenderMixin {
|
|
||||||
|
|
||||||
// TODO: Don't use redirect
|
|
||||||
@Group(name = "entityFilter", min = 1, max = 1)
|
|
||||||
@Redirect(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;entitiesForRendering()Ljava/lang/Iterable;"))
|
|
||||||
private Iterable<Entity> filterEntities(ClientLevel world) {
|
|
||||||
Iterable<Entity> entities = world.entitiesForRendering();
|
|
||||||
if (Backend.isOn()) {
|
|
||||||
ArrayList<Entity> filtered = Lists.newArrayList(entities);
|
|
||||||
|
|
||||||
filtered.removeIf(InstancedRenderRegistry::shouldSkipRender);
|
|
||||||
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
return entities;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Group(name = "entityFilter")
|
|
||||||
// @Redirect(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/ClassInstanceMultiMap;iterator()Ljava/util/Iterator;"))
|
|
||||||
// private Iterator<Entity> filterEntitiesOF(ClassInstanceMultiMap<Entity> classInheritanceMultiMap) {
|
|
||||||
// if (Backend.getInstance()
|
|
||||||
// .canUseInstancing()) {
|
|
||||||
//
|
|
||||||
// ArrayList<Entity> filtered = Lists.newArrayList(classInheritanceMultiMap);
|
|
||||||
//
|
|
||||||
// InstancedRenderRegistry r = InstancedRenderRegistry.getInstance();
|
|
||||||
// filtered.removeIf(r::shouldSkipRender);
|
|
||||||
//
|
|
||||||
// return filtered.iterator();
|
|
||||||
// }
|
|
||||||
// return classInheritanceMultiMap.iterator();
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.jozufozu.flywheel.mixin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
|
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
||||||
|
import com.jozufozu.flywheel.util.ClientLevelExtension;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.level.entity.LevelEntityGetter;
|
||||||
|
|
||||||
|
@Mixin(ClientLevel.class)
|
||||||
|
public abstract class ClientLevelMixin implements ClientLevelExtension {
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
protected abstract LevelEntityGetter<Entity> getEntities();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Entity> flywheel$getAllLoadedEntities() {
|
||||||
|
return getEntities().getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "entitiesForRendering", at = @At("RETURN"), cancellable = true)
|
||||||
|
private void filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) {
|
||||||
|
if (Backend.isOn()) {
|
||||||
|
Iterable<Entity> entities = cir.getReturnValue();
|
||||||
|
ArrayList<Entity> filtered = Lists.newArrayList(entities);
|
||||||
|
|
||||||
|
filtered.removeIf(InstancedRenderRegistry::shouldSkipRender);
|
||||||
|
|
||||||
|
cir.setReturnValue(filtered);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
package com.jozufozu.flywheel.mixin;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.OptifineHandler;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
|
||||||
import net.minecraft.client.gui.screens.VideoSettingsScreen;
|
|
||||||
|
|
||||||
@Mixin(Minecraft.class)
|
|
||||||
public class ShaderCloseMixin {
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Nullable
|
|
||||||
public Screen screen;
|
|
||||||
|
|
||||||
@Inject(at = @At("HEAD"), method = "setScreen")
|
|
||||||
private void whenScreenChanges(Screen screen, CallbackInfo info) {
|
|
||||||
if (OptifineHandler.isOptifineInstalled() && screen instanceof VideoSettingsScreen) {
|
|
||||||
Screen old = this.screen;
|
|
||||||
if (old != null && old.getClass()
|
|
||||||
.getName()
|
|
||||||
.startsWith(OptifineHandler.SHADER_PACKAGE)) {
|
|
||||||
OptifineHandler.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.jozufozu.flywheel.util;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
|
||||||
|
public interface ClientLevelExtension {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an iterator over all entities in this level.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Normally, this would be accomplished by {@link ClientLevel#entitiesForRendering()}, but the output of that
|
||||||
|
* method is filtered of entities that are rendered by flywheel. This interface provides a workaround.
|
||||||
|
* </p>
|
||||||
|
* @return An iterator over all entities in the level, including entities that are rendered by flywheel.
|
||||||
|
*/
|
||||||
|
Iterable<Entity> flywheel$getAllLoadedEntities();
|
||||||
|
|
||||||
|
static ClientLevelExtension cast(ClientLevel level) {
|
||||||
|
return (ClientLevelExtension) level;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
"BufferBuilderMixin",
|
"BufferBuilderMixin",
|
||||||
"BufferUploaderMixin",
|
"BufferUploaderMixin",
|
||||||
"CameraMixin",
|
"CameraMixin",
|
||||||
"CancelEntityRenderMixin",
|
"ClientLevelMixin",
|
||||||
"ChunkRebuildHooksMixin",
|
"ChunkRebuildHooksMixin",
|
||||||
"EntityTypeMixin",
|
"EntityTypeMixin",
|
||||||
"FixFabulousDepthMixin",
|
"FixFabulousDepthMixin",
|
||||||
|
@ -23,7 +23,6 @@
|
||||||
"PausedPartialTickAccessor",
|
"PausedPartialTickAccessor",
|
||||||
"RenderTexturesMixin",
|
"RenderTexturesMixin",
|
||||||
"RenderTypeMixin",
|
"RenderTypeMixin",
|
||||||
"ShaderCloseMixin",
|
|
||||||
"atlas.AtlasDataMixin",
|
"atlas.AtlasDataMixin",
|
||||||
"atlas.SheetDataAccessor",
|
"atlas.SheetDataAccessor",
|
||||||
"light.LightUpdateMixin",
|
"light.LightUpdateMixin",
|
||||||
|
|
Loading…
Reference in a new issue