improve portal gun perf

This commit is contained in:
craisinlord committed 2026-10-12 15:00:42 -05:00
1 parent 759f511b00
commit 0cad456efd
17 files changed
+348 -126

No files matched your search

@@ -2,6 +2,8 @@ package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.entity.WormHookProjectileEntity; import com.craisinlord.antarchy.content.entity.WormHookProjectileEntity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.content.util.WormHookRope; import com.craisinlord.antarchy.content.util.WormHookRope;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@@ -139,9 +141,9 @@ public final class WormHookTetherManager {
// the max length (which alone has no effect until the rope goes taut). // the max length (which alone has no effect until the rope goes taut).
if (reeling && dist > MIN_LEN) { if (reeling && dist > MIN_LEN) {
Vec3 pull = towardPivot.scale(-REEL_PULL_STRENGTH); Vec3 pull = towardPivot.scale(-REEL_PULL_STRENGTH);
Vec3 reelMotion = player.getDeltaMovement().add(pull); Vec3 reelMotion = getWorldMotion(player).add(pull);
reelMotion = new Vec3(reelMotion.x, Mth.clamp(reelMotion.y, -0.6D, 0.6D), reelMotion.z); reelMotion = new Vec3(reelMotion.x, Mth.clamp(reelMotion.y, -0.6D, 0.6D), reelMotion.z);
player.setDeltaMovement(reelMotion); setWorldMotion(player, reelMotion);
player.hurtMarked = true; player.hurtMarked = true;
player.fallDistance = 0; player.fallDistance = 0;
} }
@@ -158,7 +160,7 @@ public final class WormHookTetherManager {
return; return;
} }
Vec3 motion = player.getDeltaMovement(); Vec3 motion = getWorldMotion(player);
double outward = motion.dot(towardPivot); double outward = motion.dot(towardPivot);
if (outward > 0) { if (outward > 0) {
motion = motion.subtract(towardPivot.scale(outward)); motion = motion.subtract(towardPivot.scale(outward));
@@ -167,11 +169,19 @@ public final class WormHookTetherManager {
Vec3 correction = towardPivot.scale(-overshoot * PULL_CORRECTION); Vec3 correction = towardPivot.scale(-overshoot * PULL_CORRECTION);
Vec3 newMotion = motion.add(correction); Vec3 newMotion = motion.add(correction);
newMotion = new Vec3(newMotion.x, Mth.clamp(newMotion.y, -1.0D, 1.0D), newMotion.z); newMotion = new Vec3(newMotion.x, Mth.clamp(newMotion.y, -1.0D, 1.0D), newMotion.z);
player.setDeltaMovement(newMotion); setWorldMotion(player, newMotion);
player.hurtMarked = true; player.hurtMarked = true;
player.fallDistance = 0; player.fallDistance = 0;
} }
private static Vec3 getWorldMotion(ServerPlayer player) {
return AntarchyGravityRotationUtil.vecPlayerToWorld(player.getDeltaMovement(), AntarchyGravityApi.getGravityDirection(player));
}
private static void setWorldMotion(ServerPlayer player, Vec3 worldMotion) {
player.setDeltaMovement(AntarchyGravityRotationUtil.vecWorldToPlayer(worldMotion, AntarchyGravityApi.getGravityDirection(player)));
}
private static void tickFlyingSound(ServerPlayer player, TetherState state) { private static void tickFlyingSound(ServerPlayer player, TetherState state) {
if (state.flyingSoundCooldown > 0) { if (state.flyingSoundCooldown > 0) {
state.flyingSoundCooldown--; state.flyingSoundCooldown--;
@@ -1,35 +1,41 @@
package com.craisinlord.antarchy.mixins.gravity; package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.config.AntarchySettings; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.FireworkRocketEntity; import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import net.minecraft.world.item.Item;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(FireworkRocketEntity.class) @Mixin(FireworkRocketEntity.class)
/*
* Fixes firework boost dir for inverted elytra flight.
*/
public abstract class FireworkRocketGravityMixin { public abstract class FireworkRocketGravityMixin {
@Shadow @WrapOperation(method = "tick",
@Nullable at = @At(value = "INVOKE",
private LivingEntity attachedToEntity; target = "Lnet/minecraft/world/entity/LivingEntity;getLookAngle()Lnet/minecraft/world/phys/Vec3;"))
private Vec3 antarchy$fixFireworkThrustDirection(LivingEntity entity, Operation<Vec3> original) {
Vec3 look = original.call(entity);
AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(entity);
if (!direction.isInverted()) {
return look;
}
return AntarchyGravityRotationUtil.vecWorldToPlayer(look, direction);
}
@ModifyVariable(method = "tick", at = @At(value = "STORE"), ordinal = 0) @WrapOperation(method = "tick",
private Vec3 antarchy$fixFireworkThrustDirection(Vec3 velocity) { at = @At(value = "INVOKE",
if (!(this.attachedToEntity instanceof Player player)) return velocity; target = "Lnet/minecraft/world/entity/LivingEntity;getHandHoldingItemAngle(Lnet/minecraft/world/item/Item;)Lnet/minecraft/world/phys/Vec3;"))
if (!AntarchyGravityApi.isGravityInverted(player)) return velocity; private Vec3 antarchy$rotateAttachedRocketOffset(LivingEntity entity, Item item, Operation<Vec3> original) {
return AntarchyGravityRotationUtil.vecWorldToPlayer( Vec3 offset = original.call(entity, item);
velocity, AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(entity);
AntarchyGravityApi.getGravityDirection(player) if (!direction.isInverted()) {
); return offset;
}
return AntarchyGravityRotationUtil.vecPlayerToWorld(offset, direction);
} }
} }
@@ -39,6 +39,15 @@ public abstract class LivingEntityMixin {
return AntarchyGravityRotationUtil.vecWorldToPlayer(entity.position(), direction).y; return AntarchyGravityRotationUtil.vecWorldToPlayer(entity.position(), direction).y;
} }
@WrapOperation(method = "travel",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getLookAngle()Lnet/minecraft/world/phys/Vec3;"))
private Vec3 antarchy$fixTravelLookAngle(LivingEntity entity, Operation<Vec3> original) {
Vec3 look = original.call(entity);
AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(entity);
if (!direction.isInverted() || !entity.isFallFlying()) return look;
return AntarchyGravityRotationUtil.vecWorldToPlayer(look, direction);
}
@ModifyVariable(method = "checkFallDamage", at = @At("HEAD"), ordinal = 0, argsOnly = true) @ModifyVariable(method = "checkFallDamage", at = @At("HEAD"), ordinal = 0, argsOnly = true)
private double antarchy$invertLivingFallDamageY(double worldY) { private double antarchy$invertLivingFallDamageY(double worldY) {
LivingEntity entity = (LivingEntity) (Object) this; LivingEntity entity = (LivingEntity) (Object) this;
@@ -53,10 +53,18 @@ public final class PortalGunPortalRim {
} }
static void drawHiddenOutlines(Minecraft minecraft, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, float partialTick) { static void drawHiddenOutlines(Minecraft minecraft, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, float partialTick) {
drawHiddenOutlines(minecraft, cameraPos, viewMatrix, projectionMatrix, partialTick, null);
}
static void drawHiddenOutlines(Minecraft minecraft, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, float partialTick,
PortalGunPortalEntity excluded) {
if (minecraft.level == null || minecraft.player == null) { if (minecraft.level == null || minecraft.player == null) {
return; return;
} }
List<PortalGunPortalEntity> ownedPortals = collectOwnedPortals(minecraft); List<PortalGunPortalEntity> ownedPortals = collectOwnedPortals(minecraft);
if (excluded != null) {
ownedPortals.remove(excluded);
}
if (ownedPortals.isEmpty()) { if (ownedPortals.isEmpty()) {
return; return;
} }
@@ -14,6 +14,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexSorting; import com.mojang.blaze3d.vertex.VertexSorting;
import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@@ -53,7 +54,8 @@ public final class PortalGunPortalSceneRenderer {
public static void render(Minecraft minecraft, Camera camera, float partialTick, Matrix4f viewMatrix, public static void render(Minecraft minecraft, Camera camera, float partialTick, Matrix4f viewMatrix,
Matrix4f projection, Matrix4f skyProjection, Frustum frustum, Matrix4f projection, Matrix4f skyProjection, Frustum frustum,
PortalTerrain terrain, Consumer<PoseStack> crossingEntities) { PortalTerrain terrain, Consumer<PoseStack> crossingEntities,
UUID sourceId, UUID destinationId, int viewLevel) {
LevelRenderer renderer = minecraft.levelRenderer; LevelRenderer renderer = minecraft.levelRenderer;
LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) renderer; LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) renderer;
LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer; LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer;
@@ -94,8 +96,10 @@ public final class PortalGunPortalSceneRenderer {
RenderSystem.setProjectionMatrix(skyProjection, VertexSorting.DISTANCE_TO_ORIGIN); RenderSystem.setProjectionMatrix(skyProjection, VertexSorting.DISTANCE_TO_ORIGIN);
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick); FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick);
RenderSystem.setShader(GameRenderer::getPositionShader); RenderSystem.setShader(GameRenderer::getPositionShader);
renderer.renderSky(viewMatrix, skyProjection, partialTick, camera, foggy, if (irisPipelineInstalled || PortalGunPortalSkyQueries.shouldRenderSky(sourceId, destinationId, viewLevel)) {
() -> FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick)); renderer.renderSky(viewMatrix, skyProjection, partialTick, camera, foggy,
() -> FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick));
}
} }
RenderSystem.setProjectionMatrix(projection, VertexSorting.DISTANCE_TO_ORIGIN); RenderSystem.setProjectionMatrix(projection, VertexSorting.DISTANCE_TO_ORIGIN);
@@ -144,6 +148,9 @@ public final class PortalGunPortalSceneRenderer {
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers)); renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers));
} }
buffers.endBatch(); buffers.endBatch();
if (!irisPipelineInstalled) {
PortalGunPortalSkyQueries.querySkyVisibility(sourceId, destinationId, viewLevel);
}
{ {
terrain.drawLayer(minecraft, RenderType.translucent(), cameraPosition, viewMatrix, projection); terrain.drawLayer(minecraft, RenderType.translucent(), cameraPosition, viewMatrix, projection);
@@ -0,0 +1,129 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexSorting;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.client.renderer.GameRenderer;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
public final class PortalGunPortalSkyQueries {
private static final int MAX_ENTRIES = 128;
private static final LinkedHashMap<Key, QueryState> QUERIES = new LinkedHashMap<>(16, 0.75F, true);
private PortalGunPortalSkyQueries() {
}
public static boolean shouldRenderSky(UUID source, UUID destination, int level) {
QueryState state = QUERIES.get(new Key(source, destination, level));
if (state == null) {
return true;
}
poll(state);
return !state.hasResult || state.visible;
}
public static void querySkyVisibility(UUID source, UUID destination, int level) {
Key key = new Key(source, destination, level);
QueryState state = QUERIES.computeIfAbsent(key, ignored -> new QueryState(GL15.glGenQueries()));
poll(state);
if (state.inFlight) {
trim();
return;
}
MeshData mesh = buildFarPlane();
if (mesh == null) {
trim();
return;
}
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC);
modelViewStack.pushMatrix();
GL15.glBeginQuery(GL15.GL_SAMPLES_PASSED, state.query);
try {
RenderSystem.enableDepthTest();
RenderSystem.depthMask(false);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.disableBlend();
RenderSystem.disableCull();
GL11.glDepthFunc(GL11.GL_LEQUAL);
modelViewStack.identity();
RenderSystem.applyModelViewMatrix();
RenderSystem.setProjectionMatrix(new Matrix4f(), previousSorting);
RenderSystem.setShader(GameRenderer::getPositionShader);
BufferUploader.drawWithShader(mesh);
} finally {
GL15.glEndQuery(GL15.GL_SAMPLES_PASSED);
state.inFlight = true;
GL11.glDepthFunc(previousDepthFunction);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.depthMask(true);
RenderSystem.enableCull();
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
}
trim();
}
public static void clear() {
for (QueryState state : QUERIES.values()) {
GL15.glDeleteQueries(state.query);
}
QUERIES.clear();
}
private static void poll(QueryState state) {
if (state.inFlight && GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT_AVAILABLE) != 0) {
state.visible = GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT) != 0;
state.hasResult = true;
state.inFlight = false;
}
}
private static MeshData buildFarPlane() {
BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION);
buffer.addVertex(-1.0F, -1.0F, 0.99999F);
buffer.addVertex(3.0F, -1.0F, 0.99999F);
buffer.addVertex(-1.0F, 3.0F, 0.99999F);
return buffer.build();
}
private static void trim() {
Iterator<Map.Entry<Key, QueryState>> iterator = QUERIES.entrySet().iterator();
while (QUERIES.size() > MAX_ENTRIES && iterator.hasNext()) {
QueryState state = iterator.next().getValue();
if (!state.inFlight) {
GL15.glDeleteQueries(state.query);
iterator.remove();
}
}
}
private record Key(UUID source, UUID destination, int level) {
}
private static final class QueryState {
private final int query;
private boolean inFlight;
private boolean hasResult;
private boolean visible = true;
private QueryState(int query) {
this.query = query;
}
}
}
@@ -75,6 +75,7 @@ public final class PortalGunPortalViewRenderer {
public static void releaseTargets() { public static void releaseTargets() {
PortalGunPortalVisibilityQueries.clear(); PortalGunPortalVisibilityQueries.clear();
PortalGunPortalSkyQueries.clear();
PortalGunPortalSectionViews.clear(); PortalGunPortalSectionViews.clear();
if (offscreenTarget != null) { if (offscreenTarget != null) {
offscreenTarget.destroyBuffers(); offscreenTarget.destroyBuffers();
@@ -86,10 +87,10 @@ public final class PortalGunPortalViewRenderer {
if (renderingPortalView) { if (renderingPortalView) {
return; return;
} }
renderHiddenOutlines(camera, poseMatrix, projectionMatrix, tickCounter);
if (isEnabled()) { if (isEnabled()) {
renderViews(camera, poseMatrix, projectionMatrix, tickCounter); renderViews(camera, poseMatrix, projectionMatrix, tickCounter);
} }
renderHiddenOutlines(camera, poseMatrix, projectionMatrix, tickCounter);
} }
private static void renderHiddenOutlines(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) { private static void renderHiddenOutlines(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) {
@@ -244,6 +245,8 @@ public final class PortalGunPortalViewRenderer {
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection, renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
source, destination, clip, target.width, target.height, parentLevel + 1); source, destination, clip, target.width, target.height, parentLevel + 1);
PortalGunPortalRim.drawHiddenOutlines(minecraft, transformed.cameraPos(), transformed.viewMatrix(), clippedProjection,
partialTick, destination);
if (parentLevel + 1 < MAX_VIEW_LEVELS) { if (parentLevel + 1 < MAX_VIEW_LEVELS) {
renderNestedViews(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection, renderNestedViews(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
destination, parentLevel + 1, clip, budget, target.width, target.height); destination, parentLevel + 1, clip, budget, target.width, target.height);
@@ -353,6 +356,8 @@ public final class PortalGunPortalViewRenderer {
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection, renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
source, destination, clip, target.width, target.height, 1); source, destination, clip, target.width, target.height, 1);
PortalGunPortalRim.drawHiddenOutlines(minecraft, transformed.cameraPos(), transformed.viewMatrix(), clippedProjection,
partialTick, destination);
} finally { } finally {
RenderSystem.disableScissor(); RenderSystem.disableScissor();
targetAccessor.antarchy$setMainRenderTarget(mainTarget); targetAccessor.antarchy$setMainRenderTarget(mainTarget);
@@ -378,7 +383,8 @@ public final class PortalGunPortalViewRenderer {
PortalTerrain terrain = PortalTerrain.prepare(minecraft, destination.getUUID(), exit.center(), exit.normal(), frustum); PortalTerrain terrain = PortalTerrain.prepare(minecraft, destination.getUUID(), exit.center(), exit.normal(), frustum);
PortalGunPortalSceneRenderer.render(minecraft, portalCamera, partialTick, transformed.viewMatrix(), clippedProjection, PortalGunPortalSceneRenderer.render(minecraft, portalCamera, partialTick, transformed.viewMatrix(), clippedProjection,
rootProjection, frustum, terrain, rootProjection, frustum, terrain,
poseStack -> renderCrossingEntities(minecraft, transformed.cameraPos(), partialTick, source, destination, poseStack)); poseStack -> renderCrossingEntities(minecraft, transformed.cameraPos(), partialTick, source, destination, poseStack),
source.getUUID(), destination.getUUID(), level);
} finally { } finally {
PortalGunPortalRenderState.popPortalView(false); PortalGunPortalRenderState.popPortalView(false);
} }
@@ -2,6 +2,8 @@ package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.entity.WormHookProjectileEntity; import com.craisinlord.antarchy.content.entity.WormHookProjectileEntity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.content.util.WormHookRope; import com.craisinlord.antarchy.content.util.WormHookRope;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@@ -125,9 +127,9 @@ public final class WormHookTetherManager {
if (reeling && dist > MIN_LEN) { if (reeling && dist > MIN_LEN) {
Vec3 pull = towardPivot.scale(-REEL_PULL_STRENGTH); Vec3 pull = towardPivot.scale(-REEL_PULL_STRENGTH);
Vec3 reelMotion = player.getDeltaMovement().add(pull); Vec3 reelMotion = getWorldMotion(player).add(pull);
reelMotion = new Vec3(reelMotion.x, Mth.clamp(reelMotion.y, -0.6D, 0.6D), reelMotion.z); reelMotion = new Vec3(reelMotion.x, Mth.clamp(reelMotion.y, -0.6D, 0.6D), reelMotion.z);
player.setDeltaMovement(reelMotion); setWorldMotion(player, reelMotion);
player.hurtMarked = true; player.hurtMarked = true;
player.fallDistance = 0; player.fallDistance = 0;
} }
@@ -144,7 +146,7 @@ public final class WormHookTetherManager {
return; return;
} }
Vec3 motion = player.getDeltaMovement(); Vec3 motion = getWorldMotion(player);
double outward = motion.dot(towardPivot); double outward = motion.dot(towardPivot);
if (outward > 0) { if (outward > 0) {
motion = motion.subtract(towardPivot.scale(outward)); motion = motion.subtract(towardPivot.scale(outward));
@@ -153,11 +155,19 @@ public final class WormHookTetherManager {
Vec3 correction = towardPivot.scale(-overshoot * PULL_CORRECTION); Vec3 correction = towardPivot.scale(-overshoot * PULL_CORRECTION);
Vec3 newMotion = motion.add(correction); Vec3 newMotion = motion.add(correction);
newMotion = new Vec3(newMotion.x, Mth.clamp(newMotion.y, -1.0D, 1.0D), newMotion.z); newMotion = new Vec3(newMotion.x, Mth.clamp(newMotion.y, -1.0D, 1.0D), newMotion.z);
player.setDeltaMovement(newMotion); setWorldMotion(player, newMotion);
player.hurtMarked = true; player.hurtMarked = true;
player.fallDistance = 0; player.fallDistance = 0;
} }
private static Vec3 getWorldMotion(ServerPlayer player) {
return AntarchyGravityRotationUtil.vecPlayerToWorld(player.getDeltaMovement(), AntarchyGravityApi.getGravityDirection(player));
}
private static void setWorldMotion(ServerPlayer player, Vec3 worldMotion) {
player.setDeltaMovement(AntarchyGravityRotationUtil.vecWorldToPlayer(worldMotion, AntarchyGravityApi.getGravityDirection(player)));
}
private static void tickFlyingSound(ServerPlayer player, TetherState state) { private static void tickFlyingSound(ServerPlayer player, TetherState state) {
if (state.flyingSoundCooldown > 0) { if (state.flyingSoundCooldown > 0) {
state.flyingSoundCooldown--; state.flyingSoundCooldown--;
@@ -8,11 +8,13 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
public final class PortalGunPortalRegistry { public final class PortalGunPortalRegistry {
private static final Map<Level, Map<UUID, PortalGunPortalEntity>> LEVELS = Collections.synchronizedMap(new WeakHashMap<>()); private static final Map<Level, Map<UUID, PortalGunPortalEntity>> LEVELS = Collections.synchronizedMap(new WeakHashMap<>());
private static final AtomicInteger TRACKED = new AtomicInteger();
private PortalGunPortalRegistry() { private PortalGunPortalRegistry() {
} }
@@ -26,17 +28,30 @@ public final class PortalGunPortalRegistry {
synchronized (LEVELS) { synchronized (LEVELS) {
portals = LEVELS.computeIfAbsent(level, ignored -> new ConcurrentHashMap<>()); portals = LEVELS.computeIfAbsent(level, ignored -> new ConcurrentHashMap<>());
} }
portals.put(portal.getUUID(), portal); if (portals.put(portal.getUUID(), portal) == null) {
TRACKED.incrementAndGet();
}
} }
static void untrack(PortalGunPortalEntity portal) { static void untrack(PortalGunPortalEntity portal) {
Map<UUID, PortalGunPortalEntity> portals = portalsIn(portal.level()); Map<UUID, PortalGunPortalEntity> portals = portalsIn(portal.level());
if (portals != null) { if (portals != null && portals.remove(portal.getUUID(), portal)) {
portals.remove(portal.getUUID(), portal); TRACKED.decrementAndGet();
} }
} }
public static boolean hasAny(Level level) {
if (TRACKED.get() <= 0) {
return false;
}
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
return portals != null && !portals.isEmpty();
}
public static Collection<PortalGunPortalEntity> all(Level level) { public static Collection<PortalGunPortalEntity> all(Level level) {
if (TRACKED.get() <= 0) {
return List.of();
}
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level); Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
return portals == null ? List.of() : portals.values(); return portals == null ? List.of() : portals.values();
} }
@@ -54,6 +69,9 @@ public final class PortalGunPortalRegistry {
} }
public static List<PortalGunPortalEntity> near(Level level, AABB bounds) { public static List<PortalGunPortalEntity> near(Level level, AABB bounds) {
if (TRACKED.get() <= 0) {
return List.of();
}
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level); Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
if (portals == null || portals.isEmpty()) { if (portals == null || portals.isEmpty()) {
return List.of(); return List.of();
@@ -7,10 +7,12 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
@@ -38,6 +40,7 @@ import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.block.StainedGlassBlock; import net.minecraft.world.level.block.StainedGlassBlock;
import net.minecraft.world.level.block.StainedGlassPaneBlock; import net.minecraft.world.level.block.StainedGlassPaneBlock;
import net.minecraft.world.level.block.TintedGlassBlock; import net.minecraft.world.level.block.TintedGlassBlock;
@@ -53,6 +56,7 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
private static final int MAX_TICKETED_PROJECTILES = 20; private static final int MAX_TICKETED_PROJECTILES = 20;
private static final int PROJECTILE_TICKET_LEVEL = 31; private static final int PROJECTILE_TICKET_LEVEL = 31;
private static final int MAX_CHUNK_WAIT_TICKS = 200; private static final int MAX_CHUNK_WAIT_TICKS = 200;
private static final int GENERATED_CHECK_RADIUS = 3;
private static final TicketType<UUID> PROJECTILE_TICKET = TicketType.create("antarchy_portal_projectile", Comparator.comparing(UUID::toString)); private static final TicketType<UUID> PROJECTILE_TICKET = TicketType.create("antarchy_portal_projectile", Comparator.comparing(UUID::toString));
private static final LinkedHashMap<UUID, ProjectileTicket> PROJECTILE_TICKETS = new LinkedHashMap<>(); private static final LinkedHashMap<UUID, ProjectileTicket> PROJECTILE_TICKETS = new LinkedHashMap<>();
private static final EntityDataAccessor<Integer> SIDE = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor<Integer> SIDE = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
@@ -79,6 +83,7 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
private boolean spawnSynced; private boolean spawnSynced;
private boolean ticketRegistered; private boolean ticketRegistered;
private int chunkWaitTicks; private int chunkWaitTicks;
private final Map<Long, CompletableFuture<Boolean>> generatedChecks = new HashMap<>();
public int portalWidth = 1; public int portalWidth = 1;
public int portalHeight = 2; public int portalHeight = 2;
@@ -304,24 +309,69 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
return false; return false;
} }
List<ChunkPos> path = chunksAlong(position, position.add(motion)); List<ChunkPos> path = chunksAlong(position, position.add(motion));
boolean chunkload = AntarchySettings.portalGunCanPortalProjectilesChunkload(); boolean playerArea = true;
if (chunkload) { for (ChunkPos chunk : path) {
this.updateProjectileTicket(level, path); if (!level.isPositionEntityTicking(chunk.getMiddleBlockPosition(0))) {
} else { playerArea = false;
break;
}
}
if (playerArea) {
this.releaseProjectileTicket(); this.releaseProjectileTicket();
this.chunkWaitTicks = 0;
return true;
}
if (!AntarchySettings.portalGunCanPortalProjectilesChunkload()) {
this.releaseProjectileTicket();
this.discard();
return false;
} }
for (ChunkPos chunk : path) { for (ChunkPos chunk : path) {
if (level.getChunkSource().getChunkNow(chunk.x, chunk.z) == null) { for (int dx = -GENERATED_CHECK_RADIUS; dx <= GENERATED_CHECK_RADIUS; dx++) {
if (!chunkload || ++this.chunkWaitTicks > MAX_CHUNK_WAIT_TICKS) { for (int dz = -GENERATED_CHECK_RADIUS; dz <= GENERATED_CHECK_RADIUS; dz++) {
this.discard(); ChunkPos around = new ChunkPos(chunk.x + dx, chunk.z + dz);
if (level.getChunkSource().getChunkNow(around.x, around.z) != null) {
continue;
}
Boolean generated = this.isGenerated(level, around);
if (generated == null) {
return this.waitForChunks();
}
if (!generated) {
this.discard();
return false;
}
} }
return false; }
}
this.updateProjectileTicket(level, path);
for (ChunkPos chunk : path) {
if (!level.isPositionEntityTicking(chunk.getMiddleBlockPosition(0))) {
return this.waitForChunks();
} }
} }
this.chunkWaitTicks = 0; this.chunkWaitTicks = 0;
return true; return true;
} }
private boolean waitForChunks() {
if (++this.chunkWaitTicks > MAX_CHUNK_WAIT_TICKS) {
this.discard();
}
return false;
}
private Boolean isGenerated(ServerLevel level, ChunkPos chunk) {
CompletableFuture<Boolean> check = this.generatedChecks.computeIfAbsent(chunk.toLong(), key -> level.getChunkSource().chunkMap.read(chunk)
.thenApply(tag -> tag.map(PortalGunProjectileEntity::isFullChunk).orElse(false))
.exceptionally(error -> false));
return check.isDone() ? check.join() : null;
}
private static boolean isFullChunk(CompoundTag tag) {
return ChunkStatus.byName(tag.getString("Status")).isOrAfter(ChunkStatus.FULL);
}
private static List<ChunkPos> chunksAlong(Vec3 start, Vec3 end) { private static List<ChunkPos> chunksAlong(Vec3 start, Vec3 end) {
int minX = SectionPos.blockToSectionCoord(Math.min(start.x, end.x)); int minX = SectionPos.blockToSectionCoord(Math.min(start.x, end.x));
int maxX = SectionPos.blockToSectionCoord(Math.max(start.x, end.x)); int maxX = SectionPos.blockToSectionCoord(Math.max(start.x, end.x));
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper; import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.MoverType;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
@@ -24,12 +25,21 @@ public abstract class PortalGunEntityCollisionMixin {
@Inject(method = "move", at = @At("HEAD")) @Inject(method = "move", at = @At("HEAD"))
private void antarchy$capturePortalGunMovementStart(MoverType type, Vec3 movement, CallbackInfo ci) { private void antarchy$capturePortalGunMovementStart(MoverType type, Vec3 movement, CallbackInfo ci) {
this.antarchy$portalGunMoveStartBox = ((Entity) (Object) this).getBoundingBox(); Entity entity = (Entity) (Object) this;
this.antarchy$portalGunMoveStartVelocity = ((Entity) (Object) this).getDeltaMovement(); if (!PortalGunPortalRegistry.hasAny(entity.level())) {
this.antarchy$portalGunMoveStartBox = null;
this.antarchy$portalGunMoveStartVelocity = null;
return;
}
this.antarchy$portalGunMoveStartBox = entity.getBoundingBox();
this.antarchy$portalGunMoveStartVelocity = entity.getDeltaMovement();
} }
@Inject(method = "move", at = @At("RETURN")) @Inject(method = "move", at = @At("RETURN"))
private void antarchy$teleportMobOnPortalCrossing(MoverType type, Vec3 movement, CallbackInfo ci) { private void antarchy$teleportMobOnPortalCrossing(MoverType type, Vec3 movement, CallbackInfo ci) {
if (this.antarchy$portalGunMoveStartBox == null) {
return;
}
AABB startBox = this.antarchy$portalGunMoveStartBox; AABB startBox = this.antarchy$portalGunMoveStartBox;
Vec3 startVelocity = this.antarchy$portalGunMoveStartVelocity; Vec3 startVelocity = this.antarchy$portalGunMoveStartVelocity;
this.antarchy$portalGunMoveStartBox = null; this.antarchy$portalGunMoveStartBox = null;
@@ -41,15 +51,16 @@ public abstract class PortalGunEntityCollisionMixin {
@Inject(method = "collide", at = @At("HEAD")) @Inject(method = "collide", at = @At("HEAD"))
private void antarchy$capturePortalGunStartBox(Vec3 movement, CallbackInfoReturnable<Vec3> cir) { private void antarchy$capturePortalGunStartBox(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
this.antarchy$portalGunStartBox = ((Entity) (Object) this).getBoundingBox(); Entity entity = (Entity) (Object) this;
this.antarchy$portalGunStartBox = PortalGunPortalRegistry.hasAny(entity.level()) ? entity.getBoundingBox() : null;
} }
@Inject(method = "collide", at = @At("RETURN"), cancellable = true) @Inject(method = "collide", at = @At("RETURN"), cancellable = true)
private void antarchy$restorePortalCrossingMotion(Vec3 movement, CallbackInfoReturnable<Vec3> cir) { private void antarchy$restorePortalCrossingMotion(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
Entity entity = (Entity) (Object) this;
if (this.antarchy$portalGunStartBox == null) { if (this.antarchy$portalGunStartBox == null) {
return; return;
} }
Entity entity = (Entity) (Object) this;
Vec3 collided = cir.getReturnValue(); Vec3 collided = cir.getReturnValue();
Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, collided); Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, collided);
if (adjusted != collided) { if (adjusted != collided) {
@@ -1,6 +1,7 @@
package com.craisinlord.antarchy.mixins; package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -12,10 +13,10 @@ public abstract class PortalGunEntityInWallMixin {
@Inject(method = "isInWall", at = @At("HEAD"), cancellable = true) @Inject(method = "isInWall", at = @At("HEAD"), cancellable = true)
private void antarchy$ignorePortalInWall(CallbackInfoReturnable<Boolean> cir) { private void antarchy$ignorePortalInWall(CallbackInfoReturnable<Boolean> cir) {
Entity entity = (Entity) (Object) this; Entity entity = (Entity) (Object) this;
if (entity.level() == null) { if (entity.level() == null || !PortalGunPortalRegistry.hasAny(entity.level())) {
return; return;
} }
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.near(entity.level(), entity.getBoundingBox().inflate(2.0D))) { for (PortalGunPortalEntity portal : PortalGunPortalRegistry.near(entity.level(), entity.getBoundingBox().inflate(2.0D))) {
if (portal.intersectsEntityBounds(entity) || portal.getCollisionRemovalAabbForEntity(entity).intersects(entity.getBoundingBox())) { if (portal.intersectsEntityBounds(entity) || portal.getCollisionRemovalAabbForEntity(entity).intersects(entity.getBoundingBox())) {
cir.setReturnValue(false); cir.setReturnValue(false);
return; return;
@@ -1,6 +1,7 @@
package com.craisinlord.antarchy.mixins; package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.server.network.ServerGamePacketListenerImpl;
@@ -29,7 +30,7 @@ public abstract class PortalGunPlayerMovePacketMixin {
} }
this.antarchy$portalGunMoveStartBox = null; this.antarchy$portalGunMoveStartBox = null;
this.antarchy$portalGunMoveStartPosition = null; this.antarchy$portalGunMoveStartPosition = null;
if (!packet.hasPosition() || ((ServerGamePacketListenerTeleportAccessor) this).antarchy$getAwaitingPositionFromClient() != null) { if (!packet.hasPosition() || !PortalGunPortalRegistry.hasAny(player.level()) || ((ServerGamePacketListenerTeleportAccessor) this).antarchy$getAwaitingPositionFromClient() != null) {
return; return;
} }
this.antarchy$portalGunMoveStartBox = player.getBoundingBox(); this.antarchy$portalGunMoveStartBox = player.getBoundingBox();
@@ -1,35 +1,29 @@
package com.craisinlord.antarchy.mixins.gravity; package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.config.AntarchySettings; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.FireworkRocketEntity; import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import net.minecraft.world.item.Item;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(FireworkRocketEntity.class) @Mixin(FireworkRocketEntity.class)
/*
* Fixes firework boost dir for inverted elytra flight.
*/
public abstract class FireworkRocketGravityMixin { public abstract class FireworkRocketGravityMixin {
@Shadow @WrapOperation(method = "tick",
@Nullable at = @At(value = "INVOKE",
private LivingEntity attachedToEntity; target = "Lnet/minecraft/world/entity/LivingEntity;getHandHoldingItemAngle(Lnet/minecraft/world/item/Item;)Lnet/minecraft/world/phys/Vec3;"))
private Vec3 antarchy$rotateAttachedRocketOffset(LivingEntity entity, Item item, Operation<Vec3> original) {
@ModifyVariable(method = "tick", at = @At(value = "STORE"), ordinal = 0) Vec3 offset = original.call(entity, item);
private Vec3 antarchy$fixFireworkThrustDirection(Vec3 velocity) { AntarchyGravityDirection direction = AntarchyGravityApi.getGravityDirection(entity);
if (!(this.attachedToEntity instanceof Player player)) return velocity; if (!direction.isInverted()) {
if (!AntarchyGravityApi.isGravityInverted(player)) return velocity; return offset;
return AntarchyGravityRotationUtil.vecWorldToPlayer( }
velocity, return AntarchyGravityRotationUtil.vecPlayerToWorld(offset, direction);
AntarchyGravityApi.getGravityDirection(player)
);
} }
} }
@@ -17,12 +17,6 @@ import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Camera.class) @Mixin(Camera.class)
/*
* Fixes camera pos/rot for inverted gravity and the flip anim.
* Rotation is applied before move() so third-person pull-back uses the
* correct gravity-adjusted forward direction. First-person falls through
* to the TAIL inject since setup never calls move() in that case.
*/
public abstract class CameraGravityMixin { public abstract class CameraGravityMixin {
@Shadow @Shadow
private Entity entity; private Entity entity;
@@ -37,18 +31,13 @@ public abstract class CameraGravityMixin {
private float eyeHeightOld; private float eyeHeightOld;
private float antarchy$storedTickDelta; private float antarchy$storedTickDelta;
private boolean antarchy$gravityApplied;
@Shadow @Shadow
protected abstract void setPosition(double x, double y, double z); protected abstract void setPosition(double x, double y, double z);
@Shadow
protected abstract void move(float forward, float up, float left);
@Inject(method = "setup", at = @At("HEAD")) @Inject(method = "setup", at = @At("HEAD"))
private void antarchy$storeTickDelta(BlockGetter level, Entity entity, boolean detached, boolean mirrored, float partialTick, CallbackInfo ci) { private void antarchy$storeTickDelta(BlockGetter level, Entity entity, boolean detached, boolean mirrored, float partialTick, CallbackInfo ci) {
this.antarchy$storedTickDelta = partialTick; this.antarchy$storedTickDelta = partialTick;
this.antarchy$gravityApplied = false;
} }
@Redirect( @Redirect(
@@ -88,53 +77,17 @@ public abstract class CameraGravityMixin {
); );
} }
/* @Inject(
* Intercept the move() call inside setup so the pull-back direction method = "setRotation",
* already has gravity rotation applied. This covers detached (third-person) at = @At(value = "INVOKE", target = "Lorg/joml/Quaternionf;rotationYXZ(FFF)Lorg/joml/Quaternionf;", shift = At.Shift.AFTER)
* and sleeping cameras. Sets a flag so the TAIL inject doesn't double-apply.
*/
@Redirect(
method = "setup",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(FFF)V")
) )
private void antarchy$moveWithGravity( private void antarchy$applyGravityRotation(CallbackInfo ci) {
Camera camera,
float forward,
float up,
float left,
BlockGetter level,
Entity entity,
boolean detached,
boolean mirrored,
float partialTick
) {
antarchy$applyGravityRotationOnce();
this.move(forward, up, left);
}
/*
* First-person fallthrough: setup never calls move(), so gravity rotation
* hasn't been applied yet when we reach the end of the method.
*/
@Inject(method = "setup", at = @At("TAIL"))
private void antarchy$applyGravityAtTail(BlockGetter level, Entity entity, boolean detached, boolean mirrored, float partialTick, CallbackInfo ci) {
antarchy$applyGravityRotationOnce();
}
private void antarchy$applyGravityRotationOnce() {
if (this.antarchy$gravityApplied) {
return;
}
if (this.entity == null) { if (this.entity == null) {
return; return;
} }
if (!AntarchyGravityApi.isGravityInverted(this.entity) && AntarchyGravityApi.getGravityFlipProgress(this.entity, this.antarchy$storedTickDelta) <= 0.0F) { if (AntarchyGravityApi.isGravityInverted(this.entity) || AntarchyGravityApi.getGravityFlipProgress(this.entity, this.antarchy$storedTickDelta) > 0.0F) {
this.rotation.premul(new Quaternionf().fromAxisAngleDeg(0.0F, 0.0F, -1.0F, PortalGunRollClientState.getRollDegrees(this.antarchy$storedTickDelta))); this.rotation.premul(AntarchyGravityRotationUtil.getCameraRotationQuaternion(this.entity, this.antarchy$storedTickDelta));
this.antarchy$gravityApplied = true;
return;
} }
this.rotation.premul(AntarchyGravityRotationUtil.getCameraRotationQuaternion(this.entity, this.antarchy$storedTickDelta));
this.rotation.premul(new Quaternionf().fromAxisAngleDeg(0.0F, 0.0F, -1.0F, PortalGunRollClientState.getRollDegrees(this.antarchy$storedTickDelta))); this.rotation.premul(new Quaternionf().fromAxisAngleDeg(0.0F, 0.0F, -1.0F, PortalGunRollClientState.getRollDegrees(this.antarchy$storedTickDelta)));
this.antarchy$gravityApplied = true;
} }
} }
@@ -2,11 +2,13 @@ package com.craisinlord.antarchy.forge.client;
import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import net.minecraft.client.Camera;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ViewportEvent; import net.minecraftforge.client.event.ViewportEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import org.joml.Vector3f;
@Mod.EventBusSubscriber(modid = Antarchy.MODID, value = Dist.CLIENT) @Mod.EventBusSubscriber(modid = Antarchy.MODID, value = Dist.CLIENT)
public final class GravityCameraRollHandler { public final class GravityCameraRollHandler {
@@ -15,7 +17,8 @@ public final class GravityCameraRollHandler {
@SubscribeEvent @SubscribeEvent
public static void onComputeCameraAngles(ViewportEvent.ComputeCameraAngles event) { public static void onComputeCameraAngles(ViewportEvent.ComputeCameraAngles event) {
Entity entity = event.getCamera().getEntity(); Camera camera = event.getCamera();
Entity entity = camera.getEntity();
if (entity == null) { if (entity == null) {
return; return;
} }
@@ -25,6 +28,12 @@ public final class GravityCameraRollHandler {
return; return;
} }
event.setRoll(event.getRoll() + 180.0F * flipProgress); float gravityRoll = 180.0F * flipProgress;
event.setRoll(event.getRoll() + gravityRoll);
Vector3f look = camera.getLookVector();
float radians = (float) Math.toRadians(gravityRoll);
camera.getUpVector().rotateAxis(radians, look.x(), look.y(), look.z());
camera.getLeftVector().rotateAxis(radians, look.x(), look.y(), look.z());
} }
} }
+1 -1
View File
@@ -6,7 +6,7 @@ org.gradle.configureondemand=true
minecraft_version_1_21_1=1.21.1 minecraft_version_1_21_1=1.21.1
modid=antarchy modid=antarchy
mod_id=antarchy mod_id=antarchy
mod_version=2.2.1 mod_version=2.2.2
maven_group=com.craisinlord maven_group=com.craisinlord
mixin_version=0.15.4+mixin.0.8.7 mixin_version=0.15.4+mixin.0.8.7