diff --git a/docs/content.md b/docs/content.md
old mode 100644
new mode 100755
index 90f9a4f5..f17fb9f2
--- a/docs/content.md
+++ b/docs/content.md
@@ -1,16 +1,14 @@
# Installation [installing]
## Installing the Replay Mod [replaymod]
-The **ReplayMod** requires **Minecraft Forge** to be installed.
+### Minecraft 1.14 and above
+The **ReplayMod** for Minecraft 1.14 and above requires **Fabric & Fabric API** to be installed.
-Depending on your **Replay Mod** version we recommend the following **Forge** versions:
+You can find Fabric and the installation instructions [here](https://fabricmc.net/use/). With Fabric, you also need to install the Fabric API by putting it into your mods folder. You can find Fabric API [here](https://www.curseforge.com/minecraft/mc-mods/fabric-api).
-- Replay Mod 2.0.0 or later for Minecraft 1.11: Forge [1.11-13.19.1.2189](https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.11.html)
-- Replay Mod 2.0.0 or later for Minecraft 1.10.2: Forge [1.10.2-12.18.2.2099](https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.10.2.html)
-- Replay Mod 2.0.0 or later for Minecraft 1.9.4: Forge [1.9.4-12.17.0.1976](https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.9.4.html)
-- Replay Mod 2.0.0 or later for Minecraft 1.8: Forge [1.8-11.14.4.1563](https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.8.html)
-- Replay Mod 1.0.8 or older for Minecraft 1.8: Forge [1.8-11.14.3.1450](http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.8.html)
+After installing Fabric & Fabric API, simply put the downloaded ReplayMod .jar file into the `/mods` folder of your Minecraft directory.
-Other Forge Versions might, but don't necessarily work.
+### Minecraft 1.12.2 and below
+For Minecraft 1.12.2 and below it requires **Minecraft Forge** to be installed.
If you don't know how to install Forge, follow [this tutorial](https://www.youtube.com/watch?v=4i7-RystzC4).
@@ -19,22 +17,12 @@ After installing Forge, simply put the downloaded `ReplayMod.jar` file in the `/
> **Note:** The **Replay Mod** is entirely client side and can not be installed on the servers you play on.
## Installing FFmpeg [ffmpeg]
-To use the **Replay Mod**'s [Rendering Feature](#replaying-render), you need to have FFmpeg installed.
+To render your creations with **Replay Mod** you will need to have FFmpeg installed.
### Windows [windows]
-Download the latest **FFmpeg Static Build** for your architecture from .
-Then, extract the downloaded `.7z` file in the folder you want to install FFmpeg in.
+Download the latest FFmpeg build from . Make sure to select the latest release in the dropdown next to 'Version'. This will be a number like `4.3` or `4.3.1`. Choose the highest number.
-Next, download this **Batch Script** to quickly install FFmpeg:
-
-> **Note:** If your browser warns you about the download, dismiss the message. It's a simple Batch Script which won't damage your computer.
-
-Put the downloaded script into the folder where you extracted FFmpeg and run itt by double-clicking it.
-If you see a success message in the console, you've sucessfully installed **FFmpeg**!
-
-Alternatively, starting with ReplayMod 2.0.0-b5, you can also extract the downloaded `.7z` file into a `ffmpeg` folder
-(you have to create it, it doesn't exist by default) in your `.minecraft` folder. No need to run any **Batch Script**.
-The FFmpeg executable should end up at `.minecraft/ffmpeg/bin/ffmpeg.exe`.
+In your `.minecraft` folder, create a `ffmpeg` folder. Extract the downloaded .zip file into this folder. The FFmpeg executable should end up at `.minecraft/ffmpeg/bin/ffmpeg.exe`.
### Mac OSX [mac]
On OSX, you can install **FFmpeg** with **[Homebrew](http://brew.sh/)** using `brew install ffmpeg`.
@@ -45,34 +33,10 @@ Alternatively, you can download the latest static build from Custom Objects are not available in the 2.0.0 versions. They are expected to return eventually.
-
-[YouTube](_Xdpg828fbE)
-
-### Introduction [introduction]
-**Custom Objects** are mainly meant for **video creators**. They allow you to add any image into the Replay World and animate its Position, Opacity and much more.
-
-Instead of using [Motion Tracking](https://en.wikipedia.org/wiki/Match_moving) to add text or pictures to a video, you can directly add these to the rendered file.
-
-### Adding Assets [assets]
-
-The **Asset Manager** with an example Asset
-
-In order to create **Custom Objects**, you first have to add **Assets** to your Replay File.
-Therefore, open the **Asset Manager** using the `G` key.
-
-In the **Asset Manager**, you can add **Image Files (.png, .jpg and more)**, so-called **Assets**, to the Replay.
-When clicking the **"Add"** button, a File Chooser will show up. Select the image file you want to use.
-You can give the Asset a custom name using the Text Input Field in the top right corner.
-
-All of the Assets you added can be used by **Custom Objects**.
-
-### Creating Custom Objects [creating]
-
-The **Object Manager** with some **Animation Keyframes**
-
-Using the `F` key, you can open the **Object Manager**. Initially there won't be any Objects in the **Object List**, but you can simply add an Object using the **"Add"** button.
-
-After doing so, you can name the newly created **Custom Object** using the Text Field in the upper right corner.
-To define which **Asset File** (i.e. image) the **Custom Object** should use, select the desired **Asset** from the dropdown beneath the Name Input.
-
-If you leave the **Object Manager**, you should see the image in the World somwhere near your position.
-
-### Animating Custom Objects [animating]
-
-An animated **Custom Image Object**.
-
-Of course, you don't want the image to stay at that position.
- herefore, open the **Object Manager** again and select the **Custom Object** you want to modify.
-On the lower half of the screen you will see mutliple **Input Fields**, using which you can modify various settings
-(so-called **Transformations**), for example the Object's **Position**, **Scale**, **Orientation** and **Opacity**.
-
-When editing these values, you'll notice that on the TImeline to the right, Keyframes appear.
-You can set and remove Keyframes for specific settings using its "Add Keyframe" button on the very left of the screen.
-
-The **Object Manager's Keyframe System** is very similar to the **Position Keyframe** and **Time Keyframe** System.
-
-During a **Camera Path**, the **Custom Objects** will interpolate their **Transformation Values** like **Position Keyframes** are interpolated.
-While outside the **Object Manager** you can preview the Objects' position by moving the cursor on the **Keyframe Timeline** - the Objects will interpolate to the position they'll be at that timestamp during the **Camera Path**.
-
-Once you got the hang of it, you'll be able to quickly animate **Custom Objects**.
-
## Rendering [render]
With the **Replay Mod**, you can render **Camera Paths** to videos **without** using a screen recording tool like Fraps.
To get started, first [set up your Camera Path](#replaying-paths) as described in the previous chapters.
@@ -510,29 +418,6 @@ To use **High Performance Rendering**, hold down the `Ctrl` key (`Cmd` key on M
but only after one rendered seconds (e.g. every 60 frames when rendering with 60fps)
- Resizing the Minecraft Window does not update the Rendering Gui
-### Troubleshooting [troubleshooting]
-
-> If you have trouble with rendering, please first consult the Documentation before asking for help in the Forums.
-
-#### No FFmpeg installation found [ffmpeg]
-
-The error screen that is displayed when no FFmpeg installation could be found
-
-If the **Replay Mod** tells you to install FFmpeg even though you already have, you have to manually tell the **Replay Mod** where your FFmpeg executable is located.
-
-First, get the full path to your `ffmpeg.exe` (on Windows) or `ffmpeg` executable (on Mac/Linux).
-On Windows, this path might look like `C:/ffmpeg/ffmpeg.exe`.
-
-Then, open the **"Command Line Settings" Tab** in the Render Settings and paste this path into the **left input field** and retry rendering.
-
-#### Crash while rendering [crash]
-If Minecraft crashes after a few frames of rendering, it most likely means the **FFmpeg** didn't like the **Command Line Arguments** you passed.
-If you customized the Command Line Arguments manually, re-check them - it's probably your own fault.
-
-> **Hint:** In your .minecraft folder, you'll find a file called `export.log` which contains information about FFmpeg's rendering process.
-
-If you did **not** customize the **Command Line Arguments**, you might have entered some insanely high (or low) values e.g. for Bitrate or Video Resolution. Try again with other, more reasonable values.
-
## Ambient Lighting [lighting]

The same setting, onace with **Ambient Lighting** enabled, once with **Ambient Lighting** disabled
@@ -594,84 +479,8 @@ This way, it's even simpler to add structure to your Replays.
You can **delete an Event Marker** by clicking it once to select it and then pressing the `DELETE` key.
-# Replay Center [center]
-The **Replay Center** is where you can **share** your **Minecraft Moments** with others and discover awesome **Replays by other Users**.
-To be able to use the **Replay Center**, you need an **account on ReplayMod.com**.
-
-## Authentication [auth]
-
-The **Login Screen** which is displayed upon startup
-
-
-The **Register Screen**
-
-When starting Minecraft with the **Replay Mod** installed, you will be asked to login to **ReplayMod.com**.
-If you don't want to use the **Replay Center**, you can click the **"Skip" Button** to continue without logging in.
-
-If you want to browse other users' **Replays** and **share your own Replays** however, you need to **Register an account**.
-
-> **Note:** By registering an Account on **ReplayMod.com**, you agree to the Website's [Terms of Service](https://www.replaymod.com/legal/terms)
-
-Please note that you can only create **one ReplayMod.com Account per Minecraft Account**, so choose your username wisely.
-
-Once you've registered, you're automatically going to be logged in. The Mod **remembers your login** until you manually log out from the **Replay Center**, so it won't ask you upon every startup if you logged in once.
-
-## Replay Files [files]
-
-The **Replay Center** in the Mod
-
-Once you've logged in, you can use the **"Replay Center" Button** in the Main Menu to access the **Replay Center**.
-
-In the upper half of the screen, there are **5 Buttons** for **5 Tabs** of the **Replay Center**:
-- **Recent**
- Shows the most recently uploaded Replays in the **Replay Center**
-- **Best**
- Shows the Replays with the **best rating and most downloads**
-- **Downloaded**
- Shows all of the Replays you've downloaded locally
-- **Favorited**
- Shows all of the Replays you favorited
-- **Search**
- Allows you to search for specific Replays
-
-When you've found an insteresting looking Replay, you can download it using the **"Download" Button** in the lower left corner.
-After downloading, you'll automatically join the Replay.
-
-After you've downloaded a Replay, you can rate and favorite it in the **Replay Center** using the respective buttons.
-
-> You are encouraged to rate Replays after you downloaded them to help promoting the best Replays.
-
-## Upload Replays [upload]
-
-The **Replay Upload Screen**
-
-You can share your own **Replay Files** with other users in the **Replay Center**.
-Therefore, click the **"Upload" Button** in the **Replay Viewer** after selecting a Replay.
-
-In the **Replay Upload Screen** there are **6 input fields**:
-- **Replay Name**
- The Replay's Name in the Replay Center
-- **Replay Description**
- A description of what happens in the Replay. Try to give the user a good impression why your Replay is worth downloading.
-- **Category**
- The Replay's Category, one of the following: **Survival**, **Build**, **Minigame**, **Miscellaneous**. If none of the other categories fit, use **Miscellaneous**.
-- **Tags**
- One or more tags that fit your replay, spearated by comma. **Example tags:** pvp,battle,redstone,creative
-- **Hide Server IP**
- If you've recorded a Replay on a private Server, you can remove the Server IP by checking the respective checkbox.
-- **Thumbnail**
- While you can't directly edit the Thumbnail when uploading, it is highly recommended that
- you [create a Thumbnail](#replaying-thumbnail) before sharing your Replay.
- Replays without a Thumbnail are much less likely to be downloaded by other users.
-
-When uploading a Replay File, make sure to follow the [Replay Center Rules](https://www.replaymod.com/rules).
-
# Frequently Asked Questions [faq]
-### Do I need a ReplayMod.com Account to use the mod?
-You only need a ReplayMod.com Account to access the [Replay Center](#center).
-All of the other features are also available offline.
-
### For how long can I record?
Theoretically, a Replay File can be up to **24 days, 20 hours, 30 minutes and 23 seconds** long - which is a timespan you'll probably never reach.
@@ -694,3 +503,48 @@ A Replay in which you travelled around and discovered a lot of terrain is signif
An average Replay File of **10 Minutes duration** usually is between **2MB and 10MB large**.
Replays recorded on **Minigame Servers** with lots of particle effects and world changes might be larger.
+
+# Troubleshooting [troubleshooting]
+> If you have trouble with rendering, please first consult the Documentation before asking for help in the Discord.
+
+## No FFmpeg installation found [ffmpeg]
+
+The error screen that is displayed when no FFmpeg installation could be found
+
+If you have not installed ffmpeg, please follow the steps provided [here](#installing-ffmpeg)
+
+If already have installed ffmpeg, you have to manually tell the **Replay Mod** where your FFmpeg executable is located.
+
+First, get the full path to your `ffmpeg.exe` (on Windows) or `ffmpeg` executable (on Mac/Linux).
+On Windows, this path might look like `C:/ffmpeg/ffmpeg.exe`.
+
+Then, open the **"Command Line Settings" Section** in the Render Settings and paste this path into the **left input field** and retry rendering.
+
+## Crash while rendering [crash]
+If Minecraft crashes after a few frames of rendering, it most likely means the **FFmpeg** didn't like the **Command Line Arguments** you passed.
+If you customized the Command Line Arguments manually, re-check them - it's probably your own fault.
+
+> **Hint:** In your .minecraft folder, you'll find a file called `export.log` which contains information about FFmpeg's rendering process.
+
+If you did **not** customize the **Command Line Arguments**, you might have entered some insanely high (or low) values e.g. for Bitrate or Video Resolution. Try again with other, more reasonable values.
+
+## Compatibility with other Mods [compatibility]
+### General information [general]
+In General, the Replay Mod _should_ be compatible with most Forge and Fabric Mods.
+
+### Shaders Mod [shaders]
+[Karyonix' Shaders Mod](http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/1286604-shaders-mod-updated-by-karyonix)
+is no longer compatible with Minecraft Forge starting with 1.9.4. As such it is not compatible with the Replay Mod either.
+Please use [Optifine](https://optifine.net/) instead.
+
+### Custom Main Menu [custom-main-menu]
+The [Custom Main Menu](https://www.curseforge.com/minecraft/mc-mods/custom-main-menu) mod is often used in mod packs to customize their Main Menu with a button layout fitting the background image, links to their website / bug tracker and similar.
+If you are familiar with it, the button id for the Replay Mod is: **17890234** (text: `replaymod.gui.replayviewer`).
+
+Due to the nature of this Custom Main Menu mod, buttons added to the Main Menu by 3rd party mods like the **Replay Mod** will not show up by default.
+Thus, to access the Replay Viewer/Editor/Center, you need to manually configure the position for those buttons.
+To do so, you need to modify the Custom Main Menu configuration file.
+Usually, you can find it at `.minecraft/config/CustomMainMenu/mainmenu.json` (if you're using a mod pack launcher, it normally has its own .minecraft folder).
+For an explanation of this config file, have a look at Custom Main Menu's page.
+
+You can find a list of already modified config files [here](https://gist.github.com/Johni0702/3f3fab81dbf7ada83d045d9fe8f345aa).
diff --git a/docs/package-lock.json b/docs/package-lock.json
new file mode 100644
index 00000000..436a7b88
--- /dev/null
+++ b/docs/package-lock.json
@@ -0,0 +1,23 @@
+{
+ "name": "replaymod-docs",
+ "version": "0.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "array-findindex-polyfill": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/array-findindex-polyfill/-/array-findindex-polyfill-0.1.0.tgz",
+ "integrity": "sha1-w2JmW+x2RfItejw6rJeT9xw2Iu8="
+ },
+ "md-jml": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/md-jml/-/md-jml-2.0.1.tgz",
+ "integrity": "sha1-th99rTm1JPVKCmpthnMcTRLOCDo="
+ },
+ "string.prototype.endswith": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.endswith/-/string.prototype.endswith-0.2.0.tgz",
+ "integrity": "sha1-oZwg3uUamHd+mkfhDwm+OTubunU="
+ }
+ }
+}
diff --git a/jGui b/jGui
index 4898b6db..04bcf018 160000
--- a/jGui
+++ b/jGui
@@ -1 +1 @@
-Subproject commit 4898b6dbc7674177a4a280659b450903a9efac3d
+Subproject commit 04bcf018c9404ef02b7112bf49a55af6520125a2
diff --git a/src/main/java/com/replaymod/core/LoadingPlugin.java b/src/main/java/com/replaymod/core/LoadingPlugin.java
index b8f2c690..5598ca01 100644
--- a/src/main/java/com/replaymod/core/LoadingPlugin.java
+++ b/src/main/java/com/replaymod/core/LoadingPlugin.java
@@ -26,7 +26,7 @@ package com.replaymod.core;
//$$ public class LoadingPlugin implements IFMLLoadingPlugin {
//$$
//$$ public LoadingPlugin() {
-//$$ if ((Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment")) {
+//$$ if (Launch.blackboard.get("fml.deobfuscatedEnvironment") != Boolean.FALSE) {
//$$ // Outside of the dev env, this is the job of the tweaker
//$$ MixinBootstrap.init();
//$$ }
diff --git a/src/main/java/com/replaymod/core/ReplayMod.java b/src/main/java/com/replaymod/core/ReplayMod.java
index 6e9725c3..14622a87 100644
--- a/src/main/java/com/replaymod/core/ReplayMod.java
+++ b/src/main/java/com/replaymod/core/ReplayMod.java
@@ -28,6 +28,7 @@ import net.minecraft.text.Style;
import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting;
+import net.minecraft.util.crash.CrashException;
import org.apache.commons.io.FileUtils;
//#if MC>=11400
@@ -56,6 +57,7 @@ import net.fabricmc.loader.api.FabricLoader;
//$$ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
//$$ import net.minecraftforge.versions.mcp.MCPVersion;
//#else
+//$$ import static com.replaymod.core.versions.MCVer.FML_BUS;
//$$ import net.minecraft.client.resources.IResourcePack;
//$$ import net.minecraftforge.common.config.Configuration;
//#endif
@@ -72,14 +74,12 @@ import net.fabricmc.loader.api.FabricLoader;
//$$ import net.minecraftforge.fml.common.Mod.Instance;
//$$ import net.minecraftforge.fml.common.event.FMLInitializationEvent;
//$$ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
-//$$ import static com.replaymod.core.versions.MCVer.FORGE_BUS;
//#if MC>=10800
//$$ import net.minecraftforge.fml.client.FMLClientHandler;
//#else
//$$ import com.replaymod.replay.InputReplayTimer;
//$$
//$$ import java.util.ArrayDeque;
-//$$ import static com.replaymod.core.versions.MCVer.FML_BUS;
//#endif
//#endif
//$$ import net.minecraftforge.fml.common.Mod;
@@ -544,6 +544,10 @@ public class ReplayMod implements
inRunLater = true;
try {
runnable.run();
+ } catch (CrashException e) {
+ e.printStackTrace();
+ System.err.println(e.getReport().asString());
+ mc.setCrashReport(e.getReport());
} finally {
inRunLater = false;
}
@@ -552,11 +556,11 @@ public class ReplayMod implements
//#else
//$$ if (mc.isCallingFromMinecraftThread() && inRunLater) {
//#if MC>=10800
- //$$ FORGE_BUS.register(new Object() {
+ //$$ FML_BUS.register(new Object() {
//$$ @SubscribeEvent
//$$ public void onRenderTick(TickEvent.RenderTickEvent event) {
//$$ if (event.phase == TickEvent.Phase.START) {
- //$$ FORGE_BUS.unregister(this);
+ //$$ FML_BUS.unregister(this);
//$$ defer.run();
//$$ }
//$$ }
@@ -578,6 +582,10 @@ public class ReplayMod implements
//$$ inRunLater = true;
//$$ try {
//$$ runnable.run();
+ //$$ } catch (ReportedException e) {
+ //$$ e.printStackTrace();
+ //$$ System.err.println(e.getCrashReport().getCompleteReport());
+ //$$ mc.crashed(e.getCrashReport());
//$$ } finally {
//$$ inRunLater = false;
//$$ }
diff --git a/src/main/java/com/replaymod/core/mixin/MinecraftAccessor.java b/src/main/java/com/replaymod/core/mixin/MinecraftAccessor.java
index df00bccb..1ec58296 100644
--- a/src/main/java/com/replaymod/core/mixin/MinecraftAccessor.java
+++ b/src/main/java/com/replaymod/core/mixin/MinecraftAccessor.java
@@ -8,6 +8,10 @@ import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Queue;
+//#if MC>=11400
+import java.util.concurrent.CompletableFuture;
+//#endif
+
//#if MC<11400
//$$ import java.util.concurrent.FutureTask;
//#endif
@@ -24,6 +28,13 @@ public interface MinecraftAccessor {
@Accessor("renderTickCounter")
void setTimer(RenderTickCounter value);
+ //#if MC>=11400
+ @Accessor
+ CompletableFuture getResourceReloadFuture();
+ @Accessor
+ void setResourceReloadFuture(CompletableFuture value);
+ //#endif
+
//#if MC>=11400
@Accessor
Queue getRenderTaskQueue();
diff --git a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java
index 5e680fc5..a1996d90 100644
--- a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java
+++ b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java
@@ -104,17 +104,14 @@ public class RecordingEventHandler extends EventRegistrations {
}
//#endif
- public void onPlayerJoin() {
+ public void spawnRecordingPlayer() {
try {
- packetListener.save(new PlayerSpawnS2CPacket(mc.player));
- } catch(Exception e1) {
- e1.printStackTrace();
- }
- }
-
- public void onPlayerRespawn() {
- try {
- packetListener.save(new PlayerSpawnS2CPacket(mc.player));
+ ClientPlayerEntity player = mc.player;
+ assert player != null;
+ packetListener.save(new PlayerSpawnS2CPacket(player));
+ //#if MC>=11500
+ packetListener.save(new EntityTrackerUpdateS2CPacket(player.getEntityId(), player.getDataTracker(), true));
+ //#endif
lastX = lastY = lastZ = null;
} catch(Exception e) {
e.printStackTrace();
diff --git a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java
index 497a18ec..f739ce52 100644
--- a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java
+++ b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java
@@ -81,7 +81,7 @@ public abstract class MixinNetHandlerPlayClient {
// Only add spawn packet for our own player and only if he isn't known yet
if (data.getUuid().equals(mcStatic.player.getGameProfile().getId())
&& !this.playerListEntries.containsKey(data.getUuid())) {
- handler.onPlayerJoin();
+ handler.spawnRecordingPlayer();
}
}
} catch (IOException e) {
@@ -96,7 +96,7 @@ public abstract class MixinNetHandlerPlayClient {
//$$ public void recordOwnJoin(S01PacketJoinGame packet, CallbackInfo ci) {
//$$ RecordingEventHandler handler = getRecordingEventHandler();
//$$ if (handler != null) {
- //$$ handler.onPlayerJoin();
+ //$$ handler.spawnRecordingPlayer();
//$$ }
//$$ }
//#endif
@@ -116,7 +116,7 @@ public abstract class MixinNetHandlerPlayClient {
public void recordOwnRespawn(PlayerRespawnS2CPacket packet, CallbackInfo ci) {
RecordingEventHandler handler = getRecordingEventHandler();
if (handler != null) {
- handler.onPlayerRespawn();
+ handler.spawnRecordingPlayer();
}
}
}
diff --git a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java
index 754e4553..75c105a3 100644
--- a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java
+++ b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java
@@ -95,10 +95,9 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
}
// Update export arguments to match new Preset
exportArguments.setText(newPreset.getValue());
- // If the user hasn't changed the output file by themselves,
- if (!outputFileManuallySet) {
- // generate a new output file name with updated file extension
- outputFile = generateOutputFile(newPreset);
+ // Update output file ending
+ if (outputFile != null) {
+ outputFile = conformExtension(outputFile, newPreset);
outputFileButton.setLabel(outputFile.getName());
}
updateInputs();
@@ -125,15 +124,20 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
public final GuiButton outputFileButton = new GuiButton().setMinSize(new Dimension(0, 20)).onClick(new Runnable() {
@Override
public void run() {
+ GuiFileChooserPopup popup = GuiFileChooserPopup.openSaveGui(GuiRenderSettings.this, "replaymod.gui.save",
+ encodingPresetDropdown.getSelectedValue().getFileExtension());
+ popup.setFolder(outputFile.getParentFile());
+ popup.setFileName(outputFile.getName());
Futures.addCallback(
- GuiFileChooserPopup.openSaveGui(GuiRenderSettings.this, "replaymod.gui.save",
- encodingPresetDropdown.getSelectedValue().getFileExtension()).getFuture(),
+ popup.getFuture(),
new FutureCallback() {
@Override
public void onSuccess(@Nullable File result) {
if (result != null) {
+ if (!result.getName().equals(outputFile.getName())) {
+ userDefinedOutputFileName = true;
+ }
outputFile = result;
- outputFileManuallySet = true;
outputFileButton.setLabel(result.getName());
}
}
@@ -318,7 +322,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
private final ReplayHandler replayHandler;
private final Timeline timeline;
private File outputFile;
- private boolean outputFileManuallySet;
+ private boolean userDefinedOutputFileName;
public GuiRenderSettings(ReplayHandler replayHandler, Timeline timeline) {
this.replayHandler = replayHandler;
@@ -500,14 +504,19 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
bitRateField.setValue(settings.getBitRate());
bitRateUnit.setSelected(0);
}
- if (settings.getOutputFile() == null) {
- outputFile = generateOutputFile(settings.getEncodingPreset());
- outputFileManuallySet = false;
+ File savedOutputFile = settings.getOutputFile();
+ if (savedOutputFile == null || !savedOutputFile.getParentFile().exists()) {
+ this.outputFile = generateOutputFile(settings.getEncodingPreset());
+ userDefinedOutputFileName = false;
+ } else if (savedOutputFile.exists()) {
+ String name = generateOutputFile(settings.getEncodingPreset()).getName();
+ this.outputFile = new File(savedOutputFile.isDirectory() ? savedOutputFile : savedOutputFile.getParentFile(), name);
+ userDefinedOutputFileName = false;
} else {
- outputFile = settings.getOutputFile();
- outputFileManuallySet = true;
+ this.outputFile = conformExtension(savedOutputFile, settings.getEncodingPreset());
+ userDefinedOutputFileName = true;
}
- outputFileButton.setLabel(outputFile.getName());
+ outputFileButton.setLabel(this.outputFile.getName());
nametagCheckbox.setChecked(settings.isRenderNameTags());
stabilizeYaw.setChecked(settings.isStabilizeYaw());
stabilizePitch.setChecked(settings.isStabilizePitch());
@@ -543,7 +552,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
videoHeight.getInteger(),
frameRateSlider.getValue() + 10,
bitRateField.getInteger() << (10 * bitRateUnit.getSelected()),
- serialize ? null : outputFile,
+ serialize && !userDefinedOutputFileName ? outputFile.getParentFile() : outputFile,
nametagCheckbox.isChecked(),
stabilizeYaw.isChecked() && (serialize || stabilizeYaw.isEnabled()),
stabilizePitch.isChecked() && (serialize || stabilizePitch.isEnabled()),
@@ -564,6 +573,14 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
return new File(folder, fileName + "." + encodingPreset.getFileExtension());
}
+ protected File conformExtension(File file, RenderSettings.EncodingPreset preset) {
+ String name = file.getName();
+ if (name.contains(".")) {
+ name = name.substring(0, name.lastIndexOf('.'));
+ }
+ return new File(file.getParentFile(), name + "." + preset.getFileExtension());
+ }
+
protected Path getSettingsPath() {
return ReplayModRender.instance.getRenderSettingsPath();
}
diff --git a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java
index 3a959c75..de488519 100644
--- a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java
+++ b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java
@@ -44,7 +44,9 @@ import org.lwjgl.opengl.GL11;
//#if MC>=11400
import com.replaymod.render.mixin.MainWindowAccessor;
+import net.minecraft.client.gui.screen.Screen;
import org.lwjgl.glfw.GLFW;
+import java.util.concurrent.CompletableFuture;
//#else
//$$ import net.minecraft.client.gui.ScaledResolution;
//$$ import org.lwjgl.input.Mouse;
@@ -219,7 +221,15 @@ public class VideoRenderer implements RenderInfo {
//#endif
if (!settings.isHighPerformance() || framesDone % fps == 0) {
- drawGui();
+ while (drawGui() && paused) {
+ try {
+ //noinspection BusyWait
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
}
// Updating the timer will cause the timeline player to update the game state
@@ -398,6 +408,20 @@ public class VideoRenderer implements RenderInfo {
private void executeTaskQueue() {
//#if MC>=11400
+ while (true) {
+ while (mc.overlay != null) {
+ drawGui();
+ ((MinecraftMethodAccessor) mc).replayModExecuteTaskQueue();
+ }
+
+ CompletableFuture resourceReloadFuture = ((MinecraftAccessor) mc).getResourceReloadFuture();
+ if (resourceReloadFuture != null) {
+ ((MinecraftAccessor) mc).setResourceReloadFuture(null);
+ mc.reloadResources().thenRun(() -> resourceReloadFuture.complete(null));
+ continue;
+ }
+ break;
+ }
((MCVer.MinecraftMethodAccessor) mc).replayModExecuteTaskQueue();
//#else
//$$ Queue> scheduledTasks = ((MinecraftAccessor) mc).getScheduledTasks();
@@ -430,14 +454,14 @@ public class VideoRenderer implements RenderInfo {
//#endif
}
- public void drawGui() {
+ public boolean drawGui() {
do {
//#if MC>=11400
if (GLFW.glfwWindowShouldClose(getWindow(mc).getHandle()) || ((MinecraftAccessor) mc).getCrashReporter() != null) {
//#else
//$$ if (Display.isCloseRequested() || ((MinecraftAccessor) mc).getCrashReporter() != null) {
//#endif
- return;
+ return false;
}
// Resize the GUI framebuffer if the display size changed
@@ -510,12 +534,26 @@ public class VideoRenderer implements RenderInfo {
int mouseX = (int) mc.mouse.getX() * getWindow(mc).getScaledWidth() / displayWidth;
int mouseY = (int) mc.mouse.getY() * getWindow(mc).getScaledHeight() / displayHeight;
- gui.toMinecraft().tick();
- gui.toMinecraft().render(
- //#if MC>=11600
- //$$ new MatrixStack(),
- //#endif
- mouseX, mouseY, 0);
+ if (mc.overlay != null) {
+ Screen orgScreen = mc.currentScreen;
+ try {
+ mc.currentScreen = gui.toMinecraft();
+ mc.overlay.render(
+ //#if MC>=11600
+ //$$ new MatrixStack(),
+ //#endif
+ mouseX, mouseY, 0);
+ } finally {
+ mc.currentScreen = orgScreen;
+ }
+ } else {
+ gui.toMinecraft().tick();
+ gui.toMinecraft().render(
+ //#if MC>=11600
+ //$$ new MatrixStack(),
+ //#endif
+ mouseX, mouseY, 0);
+ }
//#else
//$$ int mouseX = Mouse.getX() * scaled.getScaledWidth() / mc.displayWidth;
//$$ int mouseY = scaled.getScaledHeight() - Mouse.getY() * scaled.getScaledHeight() / mc.displayHeight - 1;
@@ -558,15 +596,9 @@ public class VideoRenderer implements RenderInfo {
//$$ Mouse.setGrabbed(false);
//$$ }
//#endif
- if (paused) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return;
- }
- }
- } while (paused && !hasFailed() && !cancelled);
+
+ return !hasFailed() && !cancelled;
+ } while (true);
}
private boolean displaySizeChanged() {
diff --git a/src/main/java/com/replaymod/replay/FullReplaySender.java b/src/main/java/com/replaymod/replay/FullReplaySender.java
index da575bea..4e8cd82a 100644
--- a/src/main/java/com/replaymod/replay/FullReplaySender.java
+++ b/src/main/java/com/replaymod/replay/FullReplaySender.java
@@ -70,11 +70,14 @@ import net.minecraft.network.packet.s2c.play.EntitySpawnGlobalS2CPacket;
//#endif
//#if MC>=11400
+import com.replaymod.core.versions.MCVer;
+import net.minecraft.network.packet.s2c.play.ChunkDataS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerActionResponseS2CPacket;
import net.minecraft.network.packet.s2c.play.OpenContainerS2CPacket;
import net.minecraft.network.packet.s2c.play.OpenWrittenBookS2CPacket;
import net.minecraft.entity.EntityType;
import net.minecraft.text.TranslatableText;
+import net.minecraft.world.chunk.light.LightingProvider;
//#else
//$$ import net.minecraft.client.resources.I18n;
//$$ import net.minecraft.entity.Entity;
@@ -459,6 +462,24 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
//#endif
}
}
+
+ //#if MC>=11400
+ if (p instanceof ChunkDataS2CPacket) {
+ Runnable doLightUpdates = () -> {
+ if (mc.world != null) {
+ LightingProvider provider = mc.world.getChunkManager().getLightingProvider();
+ while (provider.hasUpdates()) {
+ provider.doLightUpdates(Integer.MAX_VALUE, true, true);
+ }
+ }
+ };
+ if (MCVer.isOnMainThread()) {
+ doLightUpdates.run();
+ } else {
+ MCVer.scheduleOnMainThread(doLightUpdates);
+ }
+ }
+ //#endif
}
} catch (Exception e) {
// We'd rather not have a failure parsing one packet screw up the whole replay process
diff --git a/src/main/java/com/replaymod/replay/QuickReplaySender.java b/src/main/java/com/replaymod/replay/QuickReplaySender.java
index 7ee18cf5..5fa12715 100644
--- a/src/main/java/com/replaymod/replay/QuickReplaySender.java
+++ b/src/main/java/com/replaymod/replay/QuickReplaySender.java
@@ -284,7 +284,7 @@ public class QuickReplaySender extends ChannelHandlerAdapter implements ReplaySe
//$$ public void onTick(TickEvent.ClientTickEvent event) {
//$$ if (event.phase != TickEvent.Phase.START) return;
//#endif
- if (!asyncMode) return;
+ if (!asyncMode || paused()) return;
long now = System.currentTimeMillis();
long realTimePassed = now - lastAsyncUpdateTime;
diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java
index 975ab97f..c64f4e27 100644
--- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java
+++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java
@@ -578,7 +578,7 @@ public class CameraEntity
private void handleInputEvents() {
if (this.client.options.keyAttack.wasPressed() || this.client.options.keyUse.wasPressed()) {
- if (canSpectate(this.client.targetedEntity)) {
+ if (this.client.currentScreen == null && canSpectate(this.client.targetedEntity)) {
ReplayModReplay.instance.getReplayHandler().spectateEntity(
//#if MC<=10710
//$$ (EntityLivingBase)
diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java
index 705916be..a9bf7f44 100644
--- a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java
+++ b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java
@@ -100,7 +100,7 @@ public class GuiPathing {
public final GuiTexturedButton renderButton = new GuiTexturedButton().onClick(new Runnable() {
@Override
public void run() {
- if (!preparePathsForPlayback()) return;
+ if (!preparePathsForPlayback(false)) return;
// Clone the timeline passed to the settings gui as it may be stored for later rendering in a queue
SPTimeline spTimeline = mod.getCurrentTimeline();
@@ -299,11 +299,12 @@ public class GuiPathing {
if (player.isActive()) {
player.getFuture().cancel(false);
} else {
+ boolean ignoreTimeKeyframes = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT);
Path timePath = mod.getCurrentTimeline().getTimePath();
- if (!preparePathsForPlayback()) return;
+ if (!preparePathsForPlayback(ignoreTimeKeyframes)) return;
- timePath.setActive(!Keyboard.isKeyDown(Keyboard.KEY_LSHIFT));
+ timePath.setActive(!ignoreTimeKeyframes);
// Start from cursor time unless the control key is pressed (then start from beginning)
int startTime = Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)? 0 : GuiPathing.this.timeline.getCursorPosition();
ListenableFuture future = player.start(mod.getCurrentTimeline().getTimeline(), startTime);
@@ -519,10 +520,20 @@ public class GuiPathing {
}).start();
}
- private boolean preparePathsForPlayback() {
+ private boolean preparePathsForPlayback(boolean ignoreTimeKeyframes) {
SPTimeline timeline = mod.getCurrentTimeline();
timeline.getTimeline().getPaths().forEach(Path::updateAll);
+ // Make sure there are at least two position keyframes
+ if (timeline.getPositionPath().getSegments().isEmpty()) {
+ GuiInfoPopup.open(replayHandler.getOverlay(), "replaymod.chat.morekeyframes");
+ return false;
+ }
+
+ if (ignoreTimeKeyframes) {
+ return true;
+ }
+
// Make sure time keyframes's values are monotonically increasing
int lastTime = 0;
for (Keyframe keyframe : timeline.getTimePath().getKeyframes()) {
@@ -538,9 +549,8 @@ public class GuiPathing {
lastTime = time;
}
- // Make sure there are at least two position- and two time-keyframes
- if (timeline.getPositionPath().getSegments().isEmpty()
- || timeline.getTimePath().getSegments().isEmpty()) {
+ // Make sure there are at least two time keyframes
+ if (timeline.getTimePath().getSegments().isEmpty()) {
GuiInfoPopup.open(replayHandler.getOverlay(), "replaymod.chat.morekeyframes");
return false;
}
diff --git a/version.txt b/version.txt
index 2bf1c1cc..f90b1afc 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-2.3.1
+2.3.2