Update to 26.1

This commit is contained in:
Jonas Herzig
2026-03-29 11:20:59 +02:00
parent b72fad4055
commit 27d5432fff
40 changed files with 667 additions and 58 deletions

0
versions/26.1/.gitkeep Normal file
View File

View File

@@ -0,0 +1,14 @@
package com.replaymod.core.mixin;
import net.minecraft.CrashReport;
import net.minecraft.util.thread.BlockableEventLoop;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.function.Supplier;
@Mixin(BlockableEventLoop.class)
public interface BlockableEventLoopAccessor {
@Accessor
Supplier<CrashReport> getDelayedCrash();
}

View File

@@ -0,0 +1,121 @@
package com.replaymod.render.mixin;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.replaymod.render.hooks.ForceChunkLoadingHook;
import net.minecraft.client.renderer.chunk.ChunkSectionLayer;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@Mixin(SectionRenderDispatcher.class)
public abstract class Mixin_BlockOnChunkRebuilds implements ForceChunkLoadingHook.IBlockOnChunkRebuilds {
@Unique
private final ReentrantLock newMainThreadWorkLock = new ReentrantLock();
@Unique
private final Condition newMainThreadWork = newMainThreadWorkLock.newCondition();
@Unique
private long scheduledTasks = 0;
@Unique
private long doneTasks = 0;
@Inject(method = "schedule", at = @At(value = "INVOKE", target = "Lnet/minecraft/TracingExecutor;execute(Ljava/lang/Runnable;)V"))
private void incrementScheduledTasks(CallbackInfo ci) {
newMainThreadWorkLock.lock();
try {
scheduledTasks++;
} finally {
newMainThreadWorkLock.unlock();
}
}
@WrapMethod(method = "runTask")
private void incrementDoneTasks(Operation<Void> original) {
try {
original.call();
} finally {
newMainThreadWorkLock.lock();
try {
doneTasks++;
newMainThreadWork.signalAll();
} finally {
newMainThreadWorkLock.unlock();
}
}
}
@Unique
private long prevTotalDoneTasks;
@Override
public boolean uploadEverythingBlocking() {
while (true) {
long doneTasksAtStartOfLoop;
boolean allTasksDone;
newMainThreadWorkLock.lock();
try {
doneTasksAtStartOfLoop = doneTasks;
allTasksDone = scheduledTasks == doneTasks;
} finally {
newMainThreadWorkLock.unlock();
}
// Upload any buffers that may still be pending
uploadAllStagedAllocations();
// If there were no tasks still running, everything should be uploaded now.
if (allTasksDone) {
break;
}
// Otherwise we need to wait for more work to come in
newMainThreadWorkLock.lock();
try {
while (doneTasksAtStartOfLoop == doneTasks) {
newMainThreadWork.awaitUninterruptibly();
}
} finally {
newMainThreadWorkLock.unlock();
}
}
boolean doneAnything = doneTasks != prevTotalDoneTasks;
prevTotalDoneTasks = doneTasks;
return doneAnything;
}
@Unique
private void uploadAllStagedAllocations() {
copyLock.lock();
try {
// MC will only allow one buffer resize per frame (presumably to avoid lag spikes), so we'll simply call
// the method as often as there are buffers (there's only a fixed amount of 3, one per ChunkSectionLayer)
for (int i = 0; i < chunkUberBuffers.size(); i++) {
uploadGlobalGeomBuffersToGPU();
}
} finally {
copyLock.unlock();
}
}
@Shadow
@Final
private ReentrantLock copyLock;
@Shadow
public abstract void uploadGlobalGeomBuffersToGPU();
@Shadow
@Final
private Map<ChunkSectionLayer, ?> chunkUberBuffers;
}

View File

@@ -0,0 +1,14 @@
package com.replaymod.render.mixin;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.renderer.entity.player.AvatarRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(AvatarRenderer.class)
public abstract class Mixin_HideNameTags_PlayerEntity {
// 1.21.11 and below
}

View File

@@ -0,0 +1,10 @@
package com.replaymod.render.mixin;
import net.minecraft.client.renderer.GameRenderer;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(GameRenderer.class)
public abstract class Mixin_Omnidirectional_Camera {
// Now handled by Camera.enablePanoramicMode
// 1.21.11 and below
}

View File

@@ -0,0 +1,99 @@
package com.replaymod.render.mixin;
import com.replaymod.render.capturer.CubicOpenGlFrameCapturer;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Camera;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
//#if MC>=12005
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#endif
//#if MC>=11500
//#else
//$$ import org.lwjgl.opengl.GL11;
//#endif
import static com.replaymod.core.versions.MCVer.getMinecraft;
import static org.joml.Math.PI_OVER_2_f;
import static org.joml.Math.PI_f;
@Mixin(Camera.class)
public abstract class Mixin_Omnidirectional_Rotation {
@Shadow
@Final
private static Vector3f FORWARDS;
@Shadow
@Final
private static Vector3f UP;
@Shadow
@Final
private static Vector3f LEFT;
@Shadow
@Final
private Quaternionf rotation;
@Shadow
@Final
private Vector3f forwards;
@Shadow
@Final
private Vector3f up;
@Shadow
@Final
private Vector3f left;
@Shadow
private int matrixPropertiesDirty;
@Inject(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;alignWithEntity(F)V", shift = At.Shift.AFTER))
private void replayModRender_setupCubicFrameRotation(CallbackInfo ci) {
if (getHandler() == null || !(getHandler().data instanceof CubicOpenGlFrameCapturer.Data)) return;
CubicOpenGlFrameCapturer.Data data = (CubicOpenGlFrameCapturer.Data) getHandler().data;
switch (data) {
case FRONT:
break;
case RIGHT:
this.rotation.rotateY(-PI_OVER_2_f);
break;
case BACK:
this.rotation.rotateY(PI_f);
break;
case LEFT:
this.rotation.rotateY(PI_OVER_2_f);
break;
case TOP:
this.rotation.rotateX(PI_OVER_2_f);
break;
case BOTTOM:
this.rotation.rotateX(-PI_OVER_2_f);
break;
}
// From setRotation
FORWARDS.rotate(this.rotation, this.forwards);
UP.rotate(this.rotation, this.up);
LEFT.rotate(this.rotation, this.left);
this.matrixPropertiesDirty |= 3;
}
@Unique
private EntityRendererHandler getHandler() {
return ((EntityRendererHandler.IEntityRenderer) getMinecraft().gameRenderer).replayModRender_getHandler();
}
}

View File

@@ -0,0 +1,50 @@
package com.replaymod.render.mixin;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.hooks.EntityRendererHandler;
import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static com.replaymod.core.versions.MCVer.*;
//#if MC>=11400
import net.minecraft.client.Camera;
//#else
//$$ import net.minecraft.client.renderer.EntityRenderer;
//#endif
@Mixin(value = Camera.class)
public abstract class Mixin_StabilizeCamera {
@WrapOperation(method = "alignWithEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setRotation(FF)V"))
private void replayModRender_stabilizeCamera(Camera instance, float yRot, float xRot, Operation<Void> original) {
if (getHandler() != null) {
RenderSettings settings = getHandler().getSettings();
if (settings.isStabilizeYaw()) {
yRot = 0f;
}
if (settings.isStabilizePitch()) {
xRot = 0f;
}
// Roll handled in MixinCamera
}
original.call(instance, yRot, xRot);
}
@Unique
private EntityRendererHandler getHandler() {
return ((EntityRendererHandler.IEntityRenderer) getMinecraft().gameRenderer).replayModRender_getHandler();
}
}

View File

@@ -0,0 +1,32 @@
package com.replaymod.render.mixin;
import com.replaymod.render.capturer.StereoscopicOpenGlFrameCapturer;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Camera;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static com.replaymod.core.versions.MCVer.getMinecraft;
@Mixin(Camera.class)
public abstract class Mixin_Stereoscopic_Camera {
@Shadow
protected abstract void move(float forwards, float up, float right);
@Inject(method = "alignWithEntity", at = @At("RETURN"))
private void replayModRender_setupStereoscopicProjection(CallbackInfo ci) {
if (getHandler() == null || !(getHandler().data instanceof StereoscopicOpenGlFrameCapturer.Data)) return;
StereoscopicOpenGlFrameCapturer.Data data = (StereoscopicOpenGlFrameCapturer.Data) getHandler().data;
move(0f, 0f, 0.1f * (data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE ? -1 : 1));
}
@Unique
private EntityRendererHandler getHandler() {
return ((EntityRendererHandler.IEntityRenderer) getMinecraft().gameRenderer).replayModRender_getHandler();
}
}

View File

@@ -0,0 +1,30 @@
package com.replaymod.replay.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.client.Camera;
import net.minecraft.world.entity.Entity;
import org.jspecify.annotations.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.ModifyArg;
import static com.replaymod.core.versions.MCVer.getMinecraft;
@Mixin(Camera.class)
public class MixinCamera {
@Shadow
private @Nullable Entity entity;
@ModifyArg(method = "setRotation", at = @At(value = "INVOKE", target = "Lorg/joml/Quaternionf;rotationYXZ(FFF)Lorg/joml/Quaternionf;"), index = 2)
private float applyRoll(float rotZ) {
Entity entity = this.entity;
if (!(entity instanceof CameraEntity)) return rotZ;
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) getMinecraft().gameRenderer).replayModRender_getHandler();
if (handler != null && handler.getSettings().isStabilizeRoll()) return rotZ;
return rotZ - ((CameraEntity) entity).roll * (float) Math.PI / 180f;
}
}

View File

@@ -0,0 +1,57 @@
{
"schemaVersion": 1,
"id": "replaymod",
"version": "${version}",
"name": "Replay Mod",
"description": "A Mod which allows you to record, replay and share your Minecraft experience.",
"authors": [
"CrushedPixel",
"johni0702"
],
"contact": {
"homepage": "https://replaymod.com/",
"sources": "https://github.com/ReplayMod/ReplayMod"
},
"license": "GPL-3.0-or-later",
"icon": "assets/replaymod/favicon_logo.png",
"environment": "client",
"entrypoints": {
"client": [
"com.replaymod.core.ReplayModBackend"
],
"modmenu": [
"com.replaymod.core.gui.ModMenuApiImpl"
],
"frex_flawless_frames": [
"com.replaymod.render.utils.FlawlessFrames::registerConsumer"
],
"preLaunch": [
"com.replaymod.core.DummyChainLoadEntryPoint",
"com.replaymod.core.MixinExtrasInit"
],
"mm:early_risers": [
"com.replaymod.core.ReplayModMMLauncher"
]
},
"mixins": [
"mixins.jgui.json",
"mixins.nonmmlauncher.replaymod.json"
],
"depends": {
"fabricloader": ">=0.18.5",
"fabric-networking-api-v1": "*",
"fabric-key-mapping-api-v1": "*",
"fabric-resource-loader-v0": "*"
},
"custom": {
"mm:early_risers": [
"com.replaymod.core.ReplayModMMLauncher"
],
"modmenu:clientsideOnly": true
}
}