Release 2.5.1

This commit is contained in:
Jonas Herzig
2020-11-22 16:19:28 +01:00
14 changed files with 139 additions and 20 deletions

View File

@@ -3,7 +3,7 @@ import java.io.ByteArrayOutputStream
plugins {
id("fabric-loom") version "0.4-SNAPSHOT" apply false
id("com.replaymod.preprocess") version "3c46acb"
id("com.replaymod.preprocess") version "24ac087"
id("com.github.hierynomus.license") version "0.15.0"
}
@@ -115,10 +115,7 @@ fun generateVersionsJson(): Map<String, Any> {
versions.forEach {
val (thisMcVersion, modVersion, preVersion) = it.split("-") + listOf(null, null)
if (thisMcVersion == mcVersion) {
mcVersionRoot[it] = if (tagVersions.contains(it))
"See https://github.com/ReplayMod/ReplayMod/releases/$it"
else
"See https://www.replaymod.com/forum/thread/100"
mcVersionRoot[it] = "See https://www.replaymod.com/changelog.txt"
if (latest == null) latest = it
if (preVersion == null) {
if (recommended == null) recommended = it
@@ -147,13 +144,19 @@ val doRelease by tasks.registering {
listOf(version, null)
}
// Create new commit
// Merge release branch into stable but do not yet commit (we need to update the version.txt first)
command("git", "checkout", "stable")
command("git", "merge", "--no-ff", "--no-commit", "release-$version")
// Update version.txt
file("version.txt").writeText("$version\n")
command("git", "add", "version.txt")
// Finallize the merge. The message is what is later used to identify releses for building the version.json tree
val commitMessage = if (preVersion != null)
"Pre-release $preVersion of $modVersion"
else
"Release $modVersion"
file("version.txt").writeText("$version\n")
command("git", "add", "version.txt")
command("git", "commit", "-m", commitMessage)
// Generate versions.json content

View File

@@ -53,6 +53,10 @@ public class MarkerProcessor {
}
}
public static boolean producesAnyOutput(ReplayFile replayFile) throws IOException {
return !getOutputSuffixes(replayFile).isEmpty();
}
private enum OutputState {
/** A new output file has begun but not data has been written yet. */
NotYetWriting,

View File

@@ -50,7 +50,7 @@ public class GuiCreateScreenshot extends GuiRenderSettings implements Loadable {
buttonPanel.removeElement(queueButton);
renderButton.setI18nLabel("replaymod.gui.advancedscreenshots.create").onClick(() -> {
// Closing this GUI ensures that settings are saved
getMinecraft().openScreen(null);
close();
mod.runLater(() -> {
try {

View File

@@ -8,6 +8,7 @@ public final class Setting<T> extends SettingsRegistry.SettingKeys<T> {
public static final Setting<Boolean> INDICATOR = make("indicator", "indicator", true);
public static final Setting<Boolean> AUTO_START_RECORDING = make("autoStartRecording", "autostartrecording", true);
public static final Setting<Boolean> AUTO_POST_PROCESS = make("autoPostProcess", null, true);
public static final Setting<Boolean> RENAME_DIALOG = make("renameDialog", "rename_recording_dialog", true);
private static <T> Setting<T> make(String key, String displayName, T defaultValue) {
return new Setting<>(key, displayName, defaultValue);

View File

@@ -2,6 +2,7 @@ package com.replaymod.recording.gui;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.recording.Setting;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.us.myles.ViaVersion.api.Pair;
@@ -83,12 +84,13 @@ public class GuiSavingReplay {
GuiButton applyButton = new GuiButton()
.setSize(150, 20)
.setI18nLabel("replaymod.gui.done")
.onClick(() -> {
apply.forEach(Runnable::run);
close();
});
.onClick(this::apply);
panel.addElements(new VerticalLayout.Data(0.5), applyButton);
if (!core.getSettingsRegistry().get(Setting.RENAME_DIALOG)) {
apply();
}
}
private GuiTextField addOutput(Path path, ReplayMetaData metaData) {
@@ -98,6 +100,7 @@ public class GuiSavingReplay {
.setText(originalName)
.setI18nHint("replaymod.gui.delete")
.setTextColorDisabled(Colors.RED)
.onEnter(this::apply)
.setTooltip(createTooltip(path, metaData));
GuiButton clearButton = new GuiButton()
.setSize(20, 20)
@@ -133,6 +136,11 @@ public class GuiSavingReplay {
}).addElements(null, tooltip, entry);
}
private void apply() {
apply.forEach(Runnable::run);
close();
}
private void applyOutput(Path path, String newName) {
if (newName.isEmpty()) {
try {

View File

@@ -39,6 +39,7 @@ import net.minecraft.network.Packet;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.LiteralText;
import net.minecraft.util.crash.CrashReport;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -65,6 +66,7 @@ import net.minecraft.network.NetworkSide;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
@@ -266,6 +268,23 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
List<Pair<Path, ReplayMetaData>> outputPaths;
synchronized (replayFile) {
try {
if (!MarkerProcessor.producesAnyOutput(replayFile)) {
// Immediately close the saving popup, the user doesn't care about it
core.runLater(guiSavingReplay::close);
// We still have the replay, so we just save it (at least for a few weeks) in case they change their mind
String replayName = FilenameUtils.getBaseName(outputPath.getFileName().toString());
Path rawFolder = ReplayMod.instance.getRawReplayFolder();
Path rawPath = rawFolder.resolve(outputPath.getFileName());
for (int i = 1; Files.exists(rawPath); i++) {
rawPath = rawPath.resolveSibling(replayName + "." + i + ".mcpr");
}
Files.createDirectories(rawPath.getParent());
replayFile.saveTo(rawPath.toFile());
replayFile.close();
return;
}
replayFile.save();
replayFile.close();

View File

@@ -91,7 +91,7 @@ public class CameraPathExporter {
quatYaw.setFromAxisAngle(new Vector4f(0, -1, 0, (float) Math.toRadians(yaw)));
quatPitch.setFromAxisAngle(new Vector4f(-1, 0, 0, (float) Math.toRadians(pitch)));
quatRoll.setFromAxisAngle(new Vector4f(0, 0, 1, (float) Math.toRadians(roll)));
quatRoll.setFromAxisAngle(new Vector4f(0, 0, -1, (float) Math.toRadians(roll)));
Quaternion quaternion = new Quaternion(0, 0, 0, 1);
Quaternion.mul(quaternion, quatYaw, quaternion);
@@ -213,7 +213,7 @@ public class CameraPathExporter {
java.nio.file.Path videoPath = settings.getOutputFile().toPath();
java.nio.file.Path glbBasePath = Files.isDirectory(videoPath)
? videoPath.resolve("camera.glb")
: videoPath.resolveSibling(videoPath.getFileName() + ".glb");
: videoPath.resolveSibling(FilenameUtils.getBaseName(videoPath.getFileName().toString()) + ".glb");
java.nio.file.Path glbPath = glbBasePath;
for (int i = 0; Files.exists(glbPath); i++) {
String baseName = FilenameUtils.getBaseName(glbBasePath.getFileName().toString());

View File

@@ -141,7 +141,9 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
for (Channel channel : Channel.values()) {
CubicOpenGlFrame leftFrame = leftChannels.get(channel);
CubicOpenGlFrame rightFrame = rightChannels.get(channel);
result.put(channel, new ODSOpenGlFrame(leftFrame, rightFrame));
if (leftFrame != null && rightFrame != null) {
result.put(channel, new ODSOpenGlFrame(leftFrame, rightFrame));
}
}
return result;
}

View File

@@ -252,7 +252,7 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
@Override
public void run() {
// Closing this GUI ensures that settings are saved
getMinecraft().openScreen(null);
close();
try {
VideoRenderer videoRenderer = new VideoRenderer(save(false), replayHandler, timeline);
videoRenderer.renderVideo();

View File

@@ -0,0 +1,81 @@
//#if MC>=11400
package com.replaymod.render.mixin;
import org.lwjgl.system.Library;
import org.lwjgl.system.Platform;
import org.lwjgl.util.tinyexr.TinyEXR;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.function.Consumer;
import java.util.regex.Pattern;
/**
* It appears like natives on Windows cannot be loaded if one of their dependencies has already been loaded by a
* different class loader. In our case we cannot load tinyexr (on the knot class loader) because lwjgl has already
* been loaded on the system class loader.
*
* If we force the tinyexr native to load on the system class loader (by calling `Library.loadSystem(absPath)`),
* it'll load but we'll get an error when we call any of the native methods.
*
* We can't really load TinyEXR itself via the system class loader because Java does not provide any methods for
* modifying the system class path at runtime and we'd have to use JVM-specific hacks.
*
* Strangely, if we use System.loadLibrary instead of System.load, then it all just works. This mixin implements
* that workaround by finding MC's natives folder, extracting the dll from our jar into that folder and then replacing
* the context class passed to Library.loadSystem (which it uses to find dlls in jars) with Library (which is on the
* system class loader) so it cannot find the dll in our jar and falls back to using System.loadLibrary.
*/
@Mixin(value = TinyEXR.class, remap = false)
public class Mixin_WindowsWorkaroundForTinyEXRNatives {
private static final String LOAD_SYSTEM_CONSUMERS = "Lorg/lwjgl/system/Library;loadSystem(Ljava/util/function/Consumer;Ljava/util/function/Consumer;Ljava/lang/Class;Ljava/lang/String;)V";
@ModifyArg(method = "<clinit>", at = @At(value = "INVOKE", target = LOAD_SYSTEM_CONSUMERS))
private static Class<?> uglyWindowsHacks(Consumer<String> load, Consumer<String> loadLibrary, Class<?> context, String name) throws IOException {
if (Platform.get() != Platform.WINDOWS) {
return context; // works out of the box on linux
}
name = System.mapLibraryName(name);
URL libURL = context.getClassLoader().getResource(name);
if (libURL == null) {
throw new UnsatisfiedLinkError("Failed to locate library: " + name);
}
String lwjglLibName = Library.JNI_LIBRARY_NAME;
if (!lwjglLibName.endsWith(".dll")) {
lwjglLibName = System.mapLibraryName(lwjglLibName);
}
String paths = System.getProperty("java.library.path");
Path nativesDir = null;
for (String dir : Pattern.compile(File.pathSeparator).split(paths)) {
Path path = Paths.get(dir);
if (Files.isReadable(path.resolve(lwjglLibName))) {
nativesDir = path;
break;
}
}
if (nativesDir == null) {
throw new UnsatisfiedLinkError("Failed to locate natives folder in " + paths);
}
Path libPath = nativesDir.resolve(name);
try (InputStream source = libURL.openStream()) {
Files.copy(source, libPath, StandardCopyOption.REPLACE_EXISTING);
}
return Library.class;
}
}
//#endif

View File

@@ -19,6 +19,7 @@
//#endif
//#if MC>=11400
"Mixin_PreserveDepthDuringHandRendering",
"Mixin_WindowsWorkaroundForTinyEXRNatives",
"MainWindowAccessor",
//#endif
"WorldRendererAccessor",

View File

@@ -1 +1 @@
2.5.0
2.5.1

View File

@@ -309,7 +309,7 @@ dependencies {
shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8'
shadow "com.github.ReplayMod:ReplayStudio:830678c", shadeExclusions
shadow "com.github.ReplayMod:ReplayStudio:dac74cc", shadeExclusions
implementation(jGui){
transitive = false // FG 1.2 puts all MC deps into the compile configuration and we don't want to shade those