Skip to content

Commit 66505e7

Browse files
authored
Shoulder Surfing Compat (#960)
* Implements compat for shoulder surfing * The shoulder surfing camera angle is incorrect when riding on a sub-level
1 parent 9de3af3 commit 66505e7

13 files changed

Lines changed: 234 additions & 3 deletions

File tree

buildSrc/src/main/groovy/multiloader-common.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ dependencies {
120120
compileOnly("maven.modrinth:jade-addons-forge:${project.jade_addons_version}+neoforge") { transitive = false }
121121
compileOnly("maven.modrinth:moonlight:${project.moonlight_version}-neoforge")
122122
compileOnly("curse.maven:vista-1368607:$vista_version")
123+
compileOnly("maven.modrinth:shoulder-surfing-reloaded:${minecraft_version}-${project.shouldersurfing_version}+neoforge") { transitive = false }
123124
}
124125

125126
sourcesJar {

common/src/main/java/dev/ryanhcode/sable/mixin/camera/new_camera_types/CameraTypeMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class CameraTypeMixin {
4141

4242
@Invoker(value = "<init>")
4343
private static CameraType create(final String name, final int ordinal, final boolean firstPerson, final boolean mirrored) {
44-
throw new IllegalStateException("Unreachable");
44+
throw new AssertionError("Unreachable");
4545
}
4646

4747
/**
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dev.ryanhcode.sable.mixin.compatibility.shouldersurfing;
2+
3+
import com.github.exopandora.shouldersurfing.api.util.EntityHelper;
4+
import com.llamalad7.mixinextras.sugar.Local;
5+
import dev.ryanhcode.sable.Sable;
6+
import net.minecraft.client.player.LocalPlayer;
7+
import net.minecraft.world.phys.Vec3;
8+
import org.spongepowered.asm.mixin.Mixin;
9+
import org.spongepowered.asm.mixin.injection.At;
10+
import org.spongepowered.asm.mixin.injection.ModifyVariable;
11+
12+
@Mixin(EntityHelper.class)
13+
public class EntityHelperMixin {
14+
15+
@ModifyVariable(method = "lookAtTarget", at = @At("HEAD"), index = 1, argsOnly = true)
16+
private static Vec3 modifyTarget(final Vec3 original, @Local(argsOnly = true) final LocalPlayer player) {
17+
return Sable.HELPER.projectOutOfSubLevel(player.level(), original);
18+
}
19+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package dev.ryanhcode.sable.mixin.compatibility.shouldersurfing;
2+
3+
import com.github.exopandora.shouldersurfing.api.model.PickContext;
4+
import com.github.exopandora.shouldersurfing.client.ObjectPicker;
5+
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
6+
import com.llamalad7.mixinextras.sugar.Local;
7+
import dev.ryanhcode.sable.Sable;
8+
import net.minecraft.world.entity.player.Player;
9+
import net.minecraft.world.phys.Vec3;
10+
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.injection.At;
12+
import org.spongepowered.asm.mixin.injection.Redirect;
13+
14+
@Mixin(ObjectPicker.class)
15+
public class ObjectPickerMixin {
16+
17+
@Redirect(method = "pick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;distanceTo(Lnet/minecraft/world/phys/Vec3;)D"))
18+
public double distanceTo(final Vec3 instance, final Vec3 vec, @Local(argsOnly = true) final Player player) {
19+
return Math.sqrt(Sable.HELPER.distanceSquaredWithSubLevels(player.level(), instance, vec));
20+
}
21+
22+
@Redirect(method = "pickEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;distanceToSqr(Lnet/minecraft/world/phys/Vec3;)D"))
23+
public double distanceToSq(final Vec3 instance, final Vec3 vec, @Local(argsOnly = true) final PickContext context) {
24+
return Sable.HELPER.distanceSquaredWithSubLevels(context.entity().level(), instance, vec);
25+
}
26+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package dev.ryanhcode.sable.mixin.compatibility.shouldersurfing;
2+
3+
import com.github.exopandora.shouldersurfing.api.client.IClientConfig;
4+
import com.github.exopandora.shouldersurfing.api.model.CrosshairVisibility;
5+
import com.github.exopandora.shouldersurfing.api.model.Perspective;
6+
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
7+
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
8+
import com.llamalad7.mixinextras.lib.apache.commons.ArrayUtils;
9+
import com.llamalad7.mixinextras.sugar.Local;
10+
import dev.ryanhcode.sable.mixinhelpers.camera.new_camera_types.SableCameraTypes;
11+
import dev.ryanhcode.sable.mixinhelpers.compatibility.shouldersurfing.SablePerspectives;
12+
import net.minecraft.client.CameraType;
13+
import org.spongepowered.asm.mixin.Final;
14+
import org.spongepowered.asm.mixin.Mixin;
15+
import org.spongepowered.asm.mixin.Mutable;
16+
import org.spongepowered.asm.mixin.Shadow;
17+
import org.spongepowered.asm.mixin.gen.Invoker;
18+
import org.spongepowered.asm.mixin.injection.At;
19+
import org.spongepowered.asm.mixin.injection.Inject;
20+
import org.spongepowered.asm.mixin.injection.ModifyVariable;
21+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
22+
23+
@Mixin(Perspective.class)
24+
public class PerspectiveMixin {
25+
26+
@Shadow
27+
@Final
28+
@Mutable
29+
private static Perspective[] $VALUES;
30+
31+
static {
32+
final var subLevelView = create("SUB_LEVEL_VIEW", $VALUES.length, SableCameraTypes.SUB_LEVEL_VIEW, CrosshairVisibility.NEVER);
33+
34+
$VALUES = ArrayUtils.add($VALUES, subLevelView);
35+
36+
final var subLevelViewUnlocked = create("SUB_LEVEL_VIEW_UNLOCKED", $VALUES.length, SableCameraTypes.SUB_LEVEL_VIEW_UNLOCKED, CrosshairVisibility.NEVER);
37+
38+
$VALUES = ArrayUtils.add($VALUES, subLevelViewUnlocked);
39+
}
40+
41+
@SuppressWarnings("SameParameterValue")
42+
@Invoker(value = "<init>")
43+
private static Perspective create(final String name, final int ordinal, final CameraType cameraType, final CrosshairVisibility defaultCrosshairVisibility) {
44+
throw new AssertionError("Unreachable");
45+
}
46+
47+
@SuppressWarnings("ConstantValue")
48+
@WrapOperation(method = "next", at = @At(value = "INVOKE", target = "Lcom/github/exopandora/shouldersurfing/api/client/IClientConfig;replaceDefaultPerspective()Z"))
49+
public boolean nextPerspective(final IClientConfig instance, final Operation<Boolean> original) {
50+
if ((Object) this == SablePerspectives.SUB_LEVEL_VIEW || (Object) this == SablePerspectives.SUB_LEVEL_VIEW_UNLOCKED) {
51+
return false;
52+
}
53+
return original.call(instance);
54+
}
55+
56+
@ModifyVariable(method = "next", at = @At(value = "STORE"), name = "next")
57+
public Perspective next(final Perspective next, @Local(argsOnly = true) final IClientConfig config) {
58+
if (config.replaceDefaultPerspective()) {
59+
if ((Object) this == Perspective.SHOULDER_SURFING) {
60+
return SablePerspectives.SUB_LEVEL_VIEW;
61+
}
62+
} else {
63+
// The normal logic will try to wrap around to our new values, but the next one should be first person
64+
if ((Object) this == Perspective.SHOULDER_SURFING) {
65+
return Perspective.FIRST_PERSON;
66+
}
67+
68+
if ((Object) this == Perspective.THIRD_PERSON_BACK) {
69+
return SablePerspectives.SUB_LEVEL_VIEW;
70+
}
71+
}
72+
73+
if ((Object) this == SablePerspectives.SUB_LEVEL_VIEW) {
74+
return SablePerspectives.SUB_LEVEL_VIEW_UNLOCKED;
75+
}
76+
if ((Object) this == SablePerspectives.SUB_LEVEL_VIEW_UNLOCKED) {
77+
return Perspective.THIRD_PERSON_FRONT;
78+
}
79+
80+
return next;
81+
}
82+
83+
@Inject(method = "next", at = @At("TAIL"), cancellable = true)
84+
public void getNext(final CallbackInfoReturnable<Perspective> cir, @Local(name = "next") final Perspective next) {
85+
if (next == SablePerspectives.SUB_LEVEL_VIEW) {
86+
cir.setReturnValue(SablePerspectives.SUB_LEVEL_VIEW);
87+
}
88+
if (next == SablePerspectives.SUB_LEVEL_VIEW_UNLOCKED) {
89+
cir.setReturnValue(SablePerspectives.SUB_LEVEL_VIEW_UNLOCKED);
90+
}
91+
}
92+
93+
@SuppressWarnings("ConstantValue")
94+
@Inject(method = "isEnabled", at = @At("HEAD"), cancellable = true)
95+
public void isEnabled(final CallbackInfoReturnable<Boolean> cir) {
96+
if ((Object) this == SablePerspectives.SUB_LEVEL_VIEW || (Object) this == SablePerspectives.SUB_LEVEL_VIEW_UNLOCKED) {
97+
cir.setReturnValue(true);
98+
}
99+
}
100+
101+
@Inject(method = "of", at = @At("HEAD"), cancellable = true)
102+
private static void of(final CameraType cameraType, final boolean shoulderSurfing, final CallbackInfoReturnable<Perspective> cir) {
103+
if (cameraType == SableCameraTypes.SUB_LEVEL_VIEW) {
104+
cir.setReturnValue(SablePerspectives.SUB_LEVEL_VIEW);
105+
}
106+
if (cameraType == SableCameraTypes.SUB_LEVEL_VIEW_UNLOCKED) {
107+
cir.setReturnValue(SablePerspectives.SUB_LEVEL_VIEW_UNLOCKED);
108+
}
109+
}
110+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package dev.ryanhcode.sable.mixin.compatibility.shouldersurfing;
2+
3+
import com.github.exopandora.shouldersurfing.client.ShoulderSurfingCamera;
4+
import com.llamalad7.mixinextras.sugar.Local;
5+
import dev.ryanhcode.sable.Sable;
6+
import net.minecraft.world.level.BlockGetter;
7+
import net.minecraft.world.level.Level;
8+
import net.minecraft.world.phys.Vec3;
9+
import org.spongepowered.asm.mixin.Mixin;
10+
import org.spongepowered.asm.mixin.injection.At;
11+
import org.spongepowered.asm.mixin.injection.Redirect;
12+
13+
@Mixin(ShoulderSurfingCamera.class)
14+
public class ShoulderSurfingCameraMixin {
15+
16+
@Redirect(method = "maxZoom", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;distanceTo(Lnet/minecraft/world/phys/Vec3;)D"))
17+
private static double distanceTo(final Vec3 instance, final Vec3 vec, @Local(argsOnly = true) final BlockGetter level) {
18+
return level instanceof Level ? Math.sqrt(Sable.HELPER.distanceSquaredWithSubLevels((Level) level, instance, vec)) : instance.distanceTo(vec);
19+
}
20+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dev.ryanhcode.sable.mixin.compatibility.shouldersurfing;
2+
3+
import com.github.exopandora.shouldersurfing.api.client.IClientConfig;
4+
import com.github.exopandora.shouldersurfing.api.model.Perspective;
5+
import com.github.exopandora.shouldersurfing.client.ShoulderSurfingImpl;
6+
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
7+
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
8+
import dev.ryanhcode.sable.Sable;
9+
import dev.ryanhcode.sable.mixinhelpers.compatibility.shouldersurfing.SablePerspectives;
10+
import net.minecraft.client.Minecraft;
11+
import net.minecraft.world.entity.Entity;
12+
import org.spongepowered.asm.mixin.Mixin;
13+
import org.spongepowered.asm.mixin.injection.At;
14+
15+
@Mixin(ShoulderSurfingImpl.class)
16+
public class ShoulderSurfingImplMixin {
17+
18+
@WrapOperation(method = "togglePerspective", at = @At(value = "INVOKE", target = "Lcom/github/exopandora/shouldersurfing/api/model/Perspective;next(Lcom/github/exopandora/shouldersurfing/api/client/IClientConfig;)Lcom/github/exopandora/shouldersurfing/api/model/Perspective;"))
19+
public Perspective next(final Perspective instance, final IClientConfig config, final Operation<Perspective> original) {
20+
final Entity cameraEntity = Minecraft.getInstance().cameraEntity;
21+
22+
Perspective next = original.call(instance, config);
23+
while (next == SablePerspectives.SUB_LEVEL_VIEW || next == SablePerspectives.SUB_LEVEL_VIEW_UNLOCKED) {
24+
if (cameraEntity != null && Sable.HELPER.getVehicleSubLevel(cameraEntity) != null) {
25+
break;
26+
}
27+
next = original.call(next, config);
28+
}
29+
return next;
30+
}
31+
}

common/src/main/java/dev/ryanhcode/sable/mixinhelpers/camera/new_camera_types/SableCameraTypes.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
import net.minecraft.client.CameraType;
44

5-
public class SableCameraTypes {
5+
public final class SableCameraTypes {
6+
67
public static final CameraType SUB_LEVEL_VIEW = Enum.valueOf(CameraType.class, "SUB_LEVEL_VIEW");
78
public static final CameraType SUB_LEVEL_VIEW_UNLOCKED = Enum.valueOf(CameraType.class, "SUB_LEVEL_VIEW_UNLOCKED");
9+
10+
private SableCameraTypes() {
11+
}
812
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package dev.ryanhcode.sable.mixinhelpers.compatibility.shouldersurfing;
2+
3+
import com.github.exopandora.shouldersurfing.api.model.Perspective;
4+
5+
public class SablePerspectives {
6+
7+
public static final Perspective SUB_LEVEL_VIEW = Enum.valueOf(Perspective.class, "SUB_LEVEL_VIEW");
8+
public static final Perspective SUB_LEVEL_VIEW_UNLOCKED = Enum.valueOf(Perspective.class, "SUB_LEVEL_VIEW_UNLOCKED");
9+
10+
private SablePerspectives() {
11+
}
12+
}

common/src/main/resources/sable.mixins.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"clip_overwrite.ClientLevelMixin",
1818
"clip_overwrite.GameRendererMixin",
1919
"compatibility.iris.ExtendedShaderMixin",
20+
"compatibility.shouldersurfing.ObjectPickerMixin",
2021
"config.GameRendererAccessor",
2122
"debug_render.DebugRendererMixin",
2223
"debug_render.DebugScreenOverlayMixin",
@@ -107,6 +108,10 @@
107108
"compatibility.jade.BlockAccessorImplMixin",
108109
"compatibility.jade.RayTracingMixin",
109110
"compatibility.jadeaddons.CreatePluginMixin",
111+
"compatibility.shouldersurfing.EntityHelperMixin",
112+
"compatibility.shouldersurfing.PerspectiveMixin",
113+
"compatibility.shouldersurfing.ShoulderSurfingCameraMixin",
114+
"compatibility.shouldersurfing.ShoulderSurfingImplMixin",
110115
"compatibility.vista.LODMixin",
111116
"compatibility.vista.ViewFinderAccessMixin",
112117
"compatibility.vista.ViewFinderControllerMixin",

0 commit comments

Comments
 (0)