Release 2.6.4
This commit is contained in:
19
build.gradle
19
build.gradle
@@ -245,6 +245,7 @@ dependencies {
|
|||||||
11701: '1.17.1',
|
11701: '1.17.1',
|
||||||
11800: '1.18',
|
11800: '1.18',
|
||||||
11801: '1.18.1',
|
11801: '1.18.1',
|
||||||
|
11802: '1.18.2',
|
||||||
][mcVersion]
|
][mcVersion]
|
||||||
mappings 'net.fabricmc:yarn:' + [
|
mappings 'net.fabricmc:yarn:' + [
|
||||||
11404: '1.14.4+build.16',
|
11404: '1.14.4+build.16',
|
||||||
@@ -256,6 +257,7 @@ dependencies {
|
|||||||
11701: '1.17.1+build.29:v2',
|
11701: '1.17.1+build.29:v2',
|
||||||
11800: '1.18+build.1:v2',
|
11800: '1.18+build.1:v2',
|
||||||
11801: '1.18.1+build.1:v2',
|
11801: '1.18.1+build.1:v2',
|
||||||
|
11802: '1.18.2+build.1:v2',
|
||||||
][mcVersion]
|
][mcVersion]
|
||||||
modImplementation 'net.fabricmc:fabric-loader:0.12.5'
|
modImplementation 'net.fabricmc:fabric-loader:0.12.5'
|
||||||
def fabricApiVersion = [
|
def fabricApiVersion = [
|
||||||
@@ -268,6 +270,7 @@ dependencies {
|
|||||||
11701: '0.37.1+1.17',
|
11701: '0.37.1+1.17',
|
||||||
11800: '0.43.1+1.18',
|
11800: '0.43.1+1.18',
|
||||||
11801: '0.43.1+1.18',
|
11801: '0.43.1+1.18',
|
||||||
|
11802: '0.47.9+1.18.2',
|
||||||
][mcVersion]
|
][mcVersion]
|
||||||
def fabricApiModules = [
|
def fabricApiModules = [
|
||||||
"api-base",
|
"api-base",
|
||||||
@@ -338,7 +341,7 @@ dependencies {
|
|||||||
|
|
||||||
shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8'
|
shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8'
|
||||||
|
|
||||||
shadow "com.github.ReplayMod:ReplayStudio:70f59ef", shadeExclusions
|
shadow "com.github.ReplayMod:ReplayStudio:b5539d1", shadeExclusions
|
||||||
|
|
||||||
implementation(FABRIC ? dependencies.project(path: jGui.path, configuration: "namedElements") : jGui) {
|
implementation(FABRIC ? dependencies.project(path: jGui.path, configuration: "namedElements") : jGui) {
|
||||||
transitive = false // FG 1.2 puts all MC deps into the compile configuration and we don't want to shade those
|
transitive = false // FG 1.2 puts all MC deps into the compile configuration and we don't want to shade those
|
||||||
@@ -346,21 +349,27 @@ dependencies {
|
|||||||
shadow 'com.github.ReplayMod:lwjgl-utils:27dcd66'
|
shadow 'com.github.ReplayMod:lwjgl-utils:27dcd66'
|
||||||
|
|
||||||
if (FABRIC) {
|
if (FABRIC) {
|
||||||
if (mcVersion >= 11800) {
|
if (mcVersion >= 11802) {
|
||||||
|
modImplementation 'com.terraformersmc:modmenu:3.1.0'
|
||||||
|
} else if (mcVersion >= 11800) {
|
||||||
modImplementation 'com.terraformersmc:modmenu:3.0.0'
|
modImplementation 'com.terraformersmc:modmenu:3.0.0'
|
||||||
} else if (mcVersion >= 11700) {
|
} else if (mcVersion >= 11700) {
|
||||||
modImplementation 'com.terraformersmc:modmenu:2.0.0-beta.7'
|
modImplementation 'com.terraformersmc:modmenu:2.0.0-beta.7'
|
||||||
} else if (mcVersion >= 11602) {
|
} else if (mcVersion >= 11602) {
|
||||||
modImplementation 'com.terraformersmc:modmenu:1.16.8'
|
modImplementation 'com.terraformersmc:modmenu:1.16.8'
|
||||||
} else if (mcVersion >= 11600) {
|
} else if (mcVersion >= 11600) {
|
||||||
modImplementation 'io.github.prospector:modmenu:1.14.0+build.24'
|
modImplementation('com.terraformersmc:modmenu:1.14.15') {
|
||||||
|
exclude module: 'fabric-resource-loader-v0' // inappropriate version for 1.16.1
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
modImplementation 'io.github.prospector.modmenu:ModMenu:1.6.2-92'
|
modImplementation 'com.terraformersmc:modmenu:1.10.6'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mcVersion >= 11600) {
|
if (mcVersion >= 11600) {
|
||||||
modCompileOnly 'com.github.IrisShaders:Iris:1.0.0'
|
modCompileOnly("com.github.IrisShaders:Iris:1.18.x~v1.2.0") {
|
||||||
|
transitive = false // we do not want to upgrade our libs, we only need this to compile our mixins
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.11'
|
testImplementation 'junit:junit:4.11'
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ If you have a Replay in a dark setting (for example at nighttime, or in a cave)
|
|||||||
|
|
||||||
This works as a replacement for the **Night Vision Potion Effect**, without the side effect of a weird sky color.
|
This works as a replacement for the **Night Vision Potion Effect**, without the side effect of a weird sky color.
|
||||||
|
|
||||||
## Quick Mode [quickmode] (Minecraft 1.9 and up)
|
## Quick Mode [quickmode]
|
||||||

|

|
||||||
In **Quick Mode**, this clock symbol is displayed in the lower right corner of the screen.
|
In **Quick Mode**, this clock symbol is displayed in the lower right corner of the screen.
|
||||||
|
|
||||||
@@ -473,8 +473,6 @@ When you first enable **Quick Mode** in a replay, an internal reference of certa
|
|||||||
As a side effect, certain features like particles and second skin layers will not be rendered in the preview.
|
As a side effect, certain features like particles and second skin layers will not be rendered in the preview.
|
||||||
By default, **Quick Mode** is toggled with `Q`.
|
By default, **Quick Mode** is toggled with `Q`.
|
||||||
|
|
||||||
**Quick Mode** is available in ReplayMod for Minecraft 1.9.4 and up.
|
|
||||||
|
|
||||||
## Player Overview [overview]
|
## Player Overview [overview]
|
||||||

|

|
||||||
The **Player Overview** Screen
|
The **Player Overview** Screen
|
||||||
|
|||||||
2
jGui
2
jGui
Submodule jGui updated: c79b62a73e...5e41452b0e
@@ -189,6 +189,7 @@ val doRelease by tasks.registering {
|
|||||||
defaultTasks("bundleJar")
|
defaultTasks("bundleJar")
|
||||||
|
|
||||||
preprocess {
|
preprocess {
|
||||||
|
val mc11802 = createNode("1.18.2", 11802, "yarn")
|
||||||
val mc11801 = createNode("1.18.1", 11801, "yarn")
|
val mc11801 = createNode("1.18.1", 11801, "yarn")
|
||||||
val mc11701 = createNode("1.17.1", 11701, "yarn")
|
val mc11701 = createNode("1.17.1", 11701, "yarn")
|
||||||
val mc11700 = createNode("1.17", 11700, "yarn")
|
val mc11700 = createNode("1.17", 11700, "yarn")
|
||||||
@@ -208,6 +209,7 @@ preprocess {
|
|||||||
val mc10800 = createNode("1.8", 10800, "srg")
|
val mc10800 = createNode("1.8", 10800, "srg")
|
||||||
val mc10710 = createNode("1.7.10", 10710, "srg")
|
val mc10710 = createNode("1.7.10", 10710, "srg")
|
||||||
|
|
||||||
|
mc11802.link(mc11801)
|
||||||
mc11801.link(mc11701, file("versions/mapping-fabric-1.18.1-1.17.1.txt"))
|
mc11801.link(mc11701, file("versions/mapping-fabric-1.18.1-1.17.1.txt"))
|
||||||
mc11701.link(mc11700)
|
mc11701.link(mc11700)
|
||||||
mc11700.link(mc11604, file("versions/mapping-fabric-1.17-1.16.4.txt"))
|
mc11700.link(mc11604, file("versions/mapping-fabric-1.17-1.16.4.txt"))
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ val jGuiVersions = listOf(
|
|||||||
"1.17",
|
"1.17",
|
||||||
"1.17.1",
|
"1.17.1",
|
||||||
"1.18.1",
|
"1.18.1",
|
||||||
|
"1.18.2",
|
||||||
)
|
)
|
||||||
val replayModVersions = listOf(
|
val replayModVersions = listOf(
|
||||||
// "1.7.10",
|
// "1.7.10",
|
||||||
@@ -52,6 +53,7 @@ val replayModVersions = listOf(
|
|||||||
"1.17",
|
"1.17",
|
||||||
"1.17.1",
|
"1.17.1",
|
||||||
"1.18.1",
|
"1.18.1",
|
||||||
|
"1.18.2",
|
||||||
)
|
)
|
||||||
|
|
||||||
rootProject.buildFileName = "root.gradle.kts"
|
rootProject.buildFileName = "root.gradle.kts"
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import java.io.IOException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
//#if FABRIC
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
//#endif
|
||||||
|
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
//#else
|
//#else
|
||||||
@@ -34,6 +38,9 @@ public class ReplayModMixinConfigPlugin implements IMixinConfigPlugin {
|
|||||||
|
|
||||||
private final Logger logger = LogManager.getLogger("replaymod/mixin");
|
private final Logger logger = LogManager.getLogger("replaymod/mixin");
|
||||||
private final boolean hasOF = hasClass("optifine.OptiFineForgeTweaker") || hasClass("me.modmuss50.optifabric.mod.Optifabric");
|
private final boolean hasOF = hasClass("optifine.OptiFineForgeTweaker") || hasClass("me.modmuss50.optifabric.mod.Optifabric");
|
||||||
|
//#if FABRIC
|
||||||
|
private final boolean hasIris = FabricLoader.getInstance().isModLoaded("iris");
|
||||||
|
//#endif
|
||||||
|
|
||||||
{
|
{
|
||||||
logger.debug("hasOF: " + hasOF);
|
logger.debug("hasOF: " + hasOF);
|
||||||
@@ -49,6 +56,9 @@ public class ReplayModMixinConfigPlugin implements IMixinConfigPlugin {
|
|||||||
}
|
}
|
||||||
if (mixinClassName.endsWith("_OF")) return hasOF;
|
if (mixinClassName.endsWith("_OF")) return hasOF;
|
||||||
if (mixinClassName.endsWith("_NoOF")) return !hasOF;
|
if (mixinClassName.endsWith("_NoOF")) return !hasOF;
|
||||||
|
//#if FABRIC
|
||||||
|
if (mixinClassName.endsWith("_Iris")) return hasIris;
|
||||||
|
//#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.nio.file.WatchService;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.replaymod.core.utils.Utils.ensureDirectoryExists;
|
||||||
import static com.replaymod.core.versions.MCVer.getMinecraft;
|
import static com.replaymod.core.versions.MCVer.getMinecraft;
|
||||||
|
|
||||||
class SettingsRegistryBackend {
|
class SettingsRegistryBackend {
|
||||||
@@ -179,7 +180,7 @@ class SettingsRegistryBackend {
|
|||||||
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
String config = gson.toJson(root);
|
String config = gson.toJson(root);
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(configFile.getParent());
|
ensureDirectoryExists(configFile.getParent());
|
||||||
Files.write(configFile, config.getBytes(StandardCharsets.UTF_8));
|
Files.write(configFile, config.getBytes(StandardCharsets.UTF_8));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import java.net.URLDecoder;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static com.replaymod.core.utils.Utils.ensureDirectoryExists;
|
||||||
|
|
||||||
public class ReplayFoldersService {
|
public class ReplayFoldersService {
|
||||||
private final Path mcDir = MinecraftClient.getInstance().runDirectory.toPath();
|
private final Path mcDir = MinecraftClient.getInstance().runDirectory.toPath();
|
||||||
private final SettingsRegistry settings;
|
private final SettingsRegistry settings;
|
||||||
@@ -19,14 +21,14 @@ public class ReplayFoldersService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Path getReplayFolder() throws IOException {
|
public Path getReplayFolder() throws IOException {
|
||||||
return Files.createDirectories(mcDir.resolve(settings.get(Setting.RECORDING_PATH)));
|
return ensureDirectoryExists(mcDir.resolve(settings.get(Setting.RECORDING_PATH)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Folder into which replay backups are saved before the MarkerProcessor is unleashed.
|
* Folder into which replay backups are saved before the MarkerProcessor is unleashed.
|
||||||
*/
|
*/
|
||||||
public Path getRawReplayFolder() throws IOException {
|
public Path getRawReplayFolder() throws IOException {
|
||||||
return Files.createDirectories(getReplayFolder().resolve("raw"));
|
return ensureDirectoryExists(getReplayFolder().resolve("raw"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,7 +36,7 @@ public class ReplayFoldersService {
|
|||||||
* Distinct from the main folder, so they cannot be opened while they are still saving.
|
* Distinct from the main folder, so they cannot be opened while they are still saving.
|
||||||
*/
|
*/
|
||||||
public Path getRecordingFolder() throws IOException {
|
public Path getRecordingFolder() throws IOException {
|
||||||
return Files.createDirectories(getReplayFolder().resolve("recording"));
|
return ensureDirectoryExists(getReplayFolder().resolve("recording"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,7 +44,7 @@ public class ReplayFoldersService {
|
|||||||
* Distinct from the recording folder cause people kept confusing them with recordings.
|
* Distinct from the recording folder cause people kept confusing them with recordings.
|
||||||
*/
|
*/
|
||||||
public Path getCacheFolder() throws IOException {
|
public Path getCacheFolder() throws IOException {
|
||||||
Path path = Files.createDirectories(mcDir.resolve(settings.get(Setting.CACHE_PATH)));
|
Path path = ensureDirectoryExists(mcDir.resolve(settings.get(Setting.CACHE_PATH)));
|
||||||
try {
|
try {
|
||||||
Files.setAttribute(path, "dos:hidden", true);
|
Files.setAttribute(path, "dos:hidden", true);
|
||||||
} catch (UnsupportedOperationException ignored) {
|
} catch (UnsupportedOperationException ignored) {
|
||||||
|
|||||||
@@ -56,11 +56,16 @@ public class RestoreReplayGui extends AbstractGuiScreen<RestoreReplayGui> {
|
|||||||
new GuiLabel().setI18nText("replaymod.gui.restorereplay1"),
|
new GuiLabel().setI18nText("replaymod.gui.restorereplay1"),
|
||||||
new GuiLabel().setI18nText("replaymod.gui.restorereplay2", Files.getNameWithoutExtension(file.getName())),
|
new GuiLabel().setI18nText("replaymod.gui.restorereplay2", Files.getNameWithoutExtension(file.getName())),
|
||||||
new GuiLabel().setI18nText("replaymod.gui.restorereplay3"));
|
new GuiLabel().setI18nText("replaymod.gui.restorereplay3"));
|
||||||
|
|
||||||
|
LOGGER.info("Found partially saved replay, offering recovery: " + file);
|
||||||
|
|
||||||
yesButton.onClick(() -> {
|
yesButton.onClick(() -> {
|
||||||
|
LOGGER.info("Attempting recovery: " + file);
|
||||||
recoverInBackground();
|
recoverInBackground();
|
||||||
parent.display();
|
parent.display();
|
||||||
});
|
});
|
||||||
noButton.onClick(() -> {
|
noButton.onClick(() -> {
|
||||||
|
LOGGER.info("Recovery rejected, marking for deletion: " + file);
|
||||||
try {
|
try {
|
||||||
File tmp = new File(file.getParentFile(), file.getName() + ".tmp");
|
File tmp = new File(file.getParentFile(), file.getName() + ".tmp");
|
||||||
File deleted = new File(file.getParentFile(), file.getName() + ".del");
|
File deleted = new File(file.getParentFile(), file.getName() + ".del");
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ public abstract class MixinMinecraft
|
|||||||
PostRenderCallback.EVENT.invoker().postRender();
|
PostRenderCallback.EVENT.invoker().postRender();
|
||||||
}
|
}
|
||||||
//#else
|
//#else
|
||||||
|
//$$ @Shadow long systemTime;
|
||||||
//#if MC>=10904
|
//#if MC>=10904
|
||||||
//$$ @Shadow protected abstract void runTickKeyboard() throws IOException;
|
//$$ @Shadow protected abstract void runTickKeyboard() throws IOException;
|
||||||
//$$ @Shadow protected abstract void runTickMouse() throws IOException;
|
//$$ @Shadow protected abstract void runTickMouse() throws IOException;
|
||||||
@@ -80,6 +81,8 @@ public abstract class MixinMinecraft
|
|||||||
//$$ public void replayModRunTickMouse() {
|
//$$ public void replayModRunTickMouse() {
|
||||||
//$$ try {
|
//$$ try {
|
||||||
//$$ runTickMouse();
|
//$$ runTickMouse();
|
||||||
|
//$$ // Update last tick time (MC ignores inputs when there hasn't been a tick in 200ms)
|
||||||
|
//$$ systemTime = Minecraft.getSystemTime();
|
||||||
//$$ } catch (IOException e) {
|
//$$ } catch (IOException e) {
|
||||||
//$$ e.printStackTrace();
|
//$$ e.printStackTrace();
|
||||||
//$$ }
|
//$$ }
|
||||||
@@ -94,7 +97,12 @@ public abstract class MixinMinecraft
|
|||||||
//$$
|
//$$
|
||||||
//$$ @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;sendClickBlockToController(Z)V"), cancellable = true)
|
//$$ @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;sendClickBlockToController(Z)V"), cancellable = true)
|
||||||
//$$ private void doEarlyReturnFromRunTick(CallbackInfo ci) {
|
//$$ private void doEarlyReturnFromRunTick(CallbackInfo ci) {
|
||||||
//$$ if (earlyReturn) ci.cancel();
|
//$$ if (earlyReturn) {
|
||||||
|
//$$ ci.cancel();
|
||||||
|
//$$
|
||||||
|
//$$ // Update last tick time (MC ignores inputs when there hasn't been a tick in 200ms)
|
||||||
|
//$$ systemTime = Minecraft.getSystemTime();
|
||||||
|
//$$ }
|
||||||
//$$ }
|
//$$ }
|
||||||
//#endif
|
//#endif
|
||||||
//$$ @Redirect(
|
//$$ @Redirect(
|
||||||
|
|||||||
@@ -55,8 +55,13 @@ import javax.net.ssl.TrustManagerFactory;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.nio.file.attribute.FileAttribute;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
@@ -166,8 +171,45 @@ public class Utils {
|
|||||||
|
|
||||||
private static final PercentEscaper REPLAY_NAME_ENCODER = new PercentEscaper(".-_ ", false);
|
private static final PercentEscaper REPLAY_NAME_ENCODER = new PercentEscaper(".-_ ", false);
|
||||||
|
|
||||||
public static String replayNameToFileName(String replayName) {
|
public static Path replayNameToPath(Path folder, String replayName) {
|
||||||
return REPLAY_NAME_ENCODER.escape(replayName) + ".mcpr";
|
// If we can, prefer directly using the replay name as the file name
|
||||||
|
if (isUsable(folder, replayName + ".mcpr")) {
|
||||||
|
return folder.resolve(replayName + ".mcpr");
|
||||||
|
} else {
|
||||||
|
// otherwise, fall back to percent encoding
|
||||||
|
return folder.resolve(REPLAY_NAME_ENCODER.escape(replayName) + ".mcpr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a given file name is actually usable with the file system / operating system at the given folder.
|
||||||
|
*/
|
||||||
|
private static boolean isUsable(Path folder, String fileName) {
|
||||||
|
Path path = folder.resolve(fileName);
|
||||||
|
if (Files.exists(path)) {
|
||||||
|
return true; // if it already exits, it's definitely usable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, there's no sure way to know, so we just gotta try
|
||||||
|
try (OutputStream outputStream = Files.newOutputStream(path, StandardOpenOption.CREATE_NEW)) {
|
||||||
|
outputStream.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looking good, but now we gotta clean up that mess (and Anti-Virus / Cloud Sync are know to lock them)
|
||||||
|
int attempts = 0;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Files.delete(path);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (attempts++ > 100) {
|
||||||
|
LOGGER.warn("Repeatedly failed to clean up temporary test file at " + path + ": ", e);
|
||||||
|
return false; // while we were able to use it, it's taken now and we can't get it back
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String fileNameToReplayName(String fileName) {
|
public static String fileNameToReplayName(String fileName) {
|
||||||
@@ -356,4 +398,14 @@ public class Utils {
|
|||||||
configure.accept(instance);
|
configure.accept(instance);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like {@link Files#createDirectories(Path, FileAttribute[])} but doesn't explode if it's a symlink.
|
||||||
|
*/
|
||||||
|
public static Path ensureDirectoryExists(Path path) throws IOException {
|
||||||
|
// Who in their right mind thought the default behavior of throwing when the target is a link to a directory
|
||||||
|
// was the preferred behavior?! Everyone has to fall for this at least once to learn it...
|
||||||
|
// https://bugs.openjdk.java.net/browse/JDK-8130464
|
||||||
|
return Files.createDirectories(Files.exists(path) ? path.toRealPath() : path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ public class MCVer {
|
|||||||
|
|
||||||
//#if MC<=11601
|
//#if MC<=11601
|
||||||
//$$ public static Vec3d getTrackedPosition(Entity entity) {
|
//$$ public static Vec3d getTrackedPosition(Entity entity) {
|
||||||
//$$ return new Vec3d(entity.trackedX, entity.trackedY, entity.trackedZ);
|
//$$ return new Vec3d(entity.trackedX / 4096.0, entity.trackedY / 4096.0, entity.trackedZ / 4096.0);
|
||||||
//$$ }
|
//$$ }
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
|||||||
import de.johni0702.minecraft.gui.utils.lwjgl.Color;
|
import de.johni0702.minecraft.gui.utils.lwjgl.Color;
|
||||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
||||||
import net.minecraft.util.crash.CrashReport;
|
import net.minecraft.util.crash.CrashReport;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -31,6 +33,8 @@ import java.util.function.Consumer;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class GuiEditReplay extends AbstractGuiPopup<GuiEditReplay> {
|
public class GuiEditReplay extends AbstractGuiPopup<GuiEditReplay> {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger();
|
||||||
|
|
||||||
private final Path inputPath;
|
private final Path inputPath;
|
||||||
|
|
||||||
private final EditTimeline timeline;
|
private final EditTimeline timeline;
|
||||||
@@ -60,6 +64,8 @@ public class GuiEditReplay extends AbstractGuiPopup<GuiEditReplay> {
|
|||||||
super(container);
|
super(container);
|
||||||
this.inputPath = inputPath;
|
this.inputPath = inputPath;
|
||||||
|
|
||||||
|
LOGGER.info("Opening replay in editor: " + inputPath);
|
||||||
|
|
||||||
try (ReplayFile replayFile = ReplayMod.instance.files.open(inputPath)) {
|
try (ReplayFile replayFile = ReplayMod.instance.files.open(inputPath)) {
|
||||||
markers = replayFile.getMarkers().or(HashSet::new);
|
markers = replayFile.getMarkers().or(HashSet::new);
|
||||||
timeline = new EditTimeline(new HashSet<>(markers), markers -> this.markers = markers);
|
timeline = new EditTimeline(new HashSet<>(markers), markers -> this.markers = markers);
|
||||||
|
|||||||
@@ -133,7 +133,6 @@ public class MarkerProcessor {
|
|||||||
for (int i = 1; Files.exists(inputPath); i++) {
|
for (int i = 1; Files.exists(inputPath); i++) {
|
||||||
inputPath = inputPath.resolveSibling(replayName + "." + i + ".mcpr");
|
inputPath = inputPath.resolveSibling(replayName + "." + i + ".mcpr");
|
||||||
}
|
}
|
||||||
Files.createDirectories(inputPath.getParent());
|
|
||||||
Files.move(path, inputPath);
|
Files.move(path, inputPath);
|
||||||
|
|
||||||
try (ReplayFile inputReplayFile = mod.files.open(inputPath)) {
|
try (ReplayFile inputReplayFile = mod.files.open(inputPath)) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class QuickMode extends EventRegistrations implements Extra {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
replayHandler.getReplaySender().setSyncModeAndWait();
|
replayHandler.getReplaySender().setSyncModeAndWait();
|
||||||
mod.runLater(() -> {
|
mod.runLaterWithoutLock(() -> {
|
||||||
replayHandler.ensureQuickModeInitialized(() -> {
|
replayHandler.ensureQuickModeInitialized(() -> {
|
||||||
boolean enabled = !replayHandler.isQuickMode();
|
boolean enabled = !replayHandler.isQuickMode();
|
||||||
updateIndicator(replayHandler.getOverlay(), enabled);
|
updateIndicator(replayHandler.getOverlay(), enabled);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class GuiCreateScreenshot extends GuiRenderSettings implements Loadable {
|
|||||||
new GuiLabel().setI18nText("replaymod.gui.advancedscreenshots.resolution"), videoResolutionPanel,
|
new GuiLabel().setI18nText("replaymod.gui.advancedscreenshots.resolution"), videoResolutionPanel,
|
||||||
new GuiLabel().setI18nText("replaymod.gui.rendersettings.outputfile"), outputFileButton);
|
new GuiLabel().setI18nText("replaymod.gui.rendersettings.outputfile"), outputFileButton);
|
||||||
|
|
||||||
resetChildren(advancedPanel).addElements(null, nametagCheckbox, new GuiPanel().setLayout(
|
resetChildren(advancedPanel).addElements(null, nametagCheckbox, alphaCheckbox , new GuiPanel().setLayout(
|
||||||
new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(15))
|
new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(15))
|
||||||
.addElements(new GridLayout.Data(0, 0.5),
|
.addElements(new GridLayout.Data(0, 0.5),
|
||||||
new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel,
|
new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel,
|
||||||
|
|||||||
@@ -155,9 +155,9 @@ public class GuiSavingReplay {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Path replaysFolder = core.folders.getReplayFolder();
|
Path replaysFolder = core.folders.getReplayFolder();
|
||||||
Path newPath = replaysFolder.resolve(Utils.replayNameToFileName(newName));
|
Path newPath = Utils.replayNameToPath(replaysFolder, newName);
|
||||||
for (int i = 1; Files.exists(newPath); i++) {
|
for (int i = 1; Files.exists(newPath); i++) {
|
||||||
newPath = replaysFolder.resolve(Utils.replayNameToFileName(newName + " (" + i + ")"));
|
newPath = Utils.replayNameToPath(replaysFolder, newName + " (" + i + ")");
|
||||||
}
|
}
|
||||||
Files.move(path, newPath);
|
Files.move(path, newPath);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public class ConnectionEventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String name = sdf.format(Calendar.getInstance().getTime());
|
String name = sdf.format(Calendar.getInstance().getTime());
|
||||||
Path outputPath = core.folders.getRecordingFolder().resolve(Utils.replayNameToFileName(name));
|
Path outputPath = Utils.replayNameToPath(core.folders.getRecordingFolder(), name);
|
||||||
ReplayFile replayFile = core.files.open(outputPath);
|
ReplayFile replayFile = core.files.open(outputPath);
|
||||||
|
|
||||||
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());
|
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ public abstract class MixinMouseHelper {
|
|||||||
long _p0, double _p1, double _p2,
|
long _p0, double _p1, double _p2,
|
||||||
CallbackInfo ci,
|
CallbackInfo ci,
|
||||||
double _l1,
|
double _l1,
|
||||||
//#if MC>=11400
|
//#if MC>=11802
|
||||||
|
//$$ int yOffsetAccumulated
|
||||||
|
//#elseif MC>=11400
|
||||||
float yOffsetAccumulated
|
float yOffsetAccumulated
|
||||||
//#else
|
//#else
|
||||||
//$$ double yOffsetAccumulated
|
//$$ double yOffsetAccumulated
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
//#if MC>=11802
|
||||||
|
//$$ import net.minecraft.util.registry.RegistryEntry;
|
||||||
|
//#endif
|
||||||
|
|
||||||
//#if MC>=11600
|
//#if MC>=11600
|
||||||
import net.minecraft.util.registry.RegistryKey;
|
import net.minecraft.util.registry.RegistryKey;
|
||||||
import net.minecraft.world.MutableWorldProperties;
|
import net.minecraft.world.MutableWorldProperties;
|
||||||
@@ -52,7 +56,12 @@ public abstract class MixinWorldClient extends World implements RecordingEventHa
|
|||||||
//#if MC<11602
|
//#if MC<11602
|
||||||
//$$ RegistryKey<DimensionType> registryKey2,
|
//$$ RegistryKey<DimensionType> registryKey2,
|
||||||
//#endif
|
//#endif
|
||||||
DimensionType dimensionType, Supplier<Profiler> profiler, boolean bl, boolean bl2, long l) {
|
//#if MC>=11802
|
||||||
|
//$$ RegistryEntry<DimensionType> dimensionType,
|
||||||
|
//#else
|
||||||
|
DimensionType dimensionType,
|
||||||
|
//#endif
|
||||||
|
Supplier<Profiler> profiler, boolean bl, boolean bl2, long l) {
|
||||||
super(mutableWorldProperties, registryKey,
|
super(mutableWorldProperties, registryKey,
|
||||||
//#if MC<11602
|
//#if MC<11602
|
||||||
//$$ registryKey2,
|
//$$ registryKey2,
|
||||||
|
|||||||
@@ -285,7 +285,6 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
|||||||
for (int i = 1; Files.exists(rawPath); i++) {
|
for (int i = 1; Files.exists(rawPath); i++) {
|
||||||
rawPath = rawPath.resolveSibling(replayName + "." + i + ".mcpr");
|
rawPath = rawPath.resolveSibling(replayName + "." + i + ".mcpr");
|
||||||
}
|
}
|
||||||
Files.createDirectories(rawPath.getParent());
|
|
||||||
replayFile.saveTo(rawPath.toFile());
|
replayFile.saveTo(rawPath.toFile());
|
||||||
replayFile.close();
|
replayFile.close();
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ import static org.lwjgl.util.tinyexr.TinyEXR.*;
|
|||||||
public class EXRWriter implements FrameConsumer<BitmapFrame> {
|
public class EXRWriter implements FrameConsumer<BitmapFrame> {
|
||||||
|
|
||||||
private final Path outputFolder;
|
private final Path outputFolder;
|
||||||
|
private final boolean keepAlpha;
|
||||||
|
|
||||||
public EXRWriter(Path outputFolder) throws IOException {
|
public EXRWriter(Path outputFolder, boolean keepAlpha) throws IOException {
|
||||||
this.outputFolder = outputFolder;
|
this.outputFolder = outputFolder;
|
||||||
|
this.keepAlpha = keepAlpha;
|
||||||
|
|
||||||
Files.createDirectories(outputFolder);
|
Files.createDirectories(outputFolder);
|
||||||
}
|
}
|
||||||
@@ -92,11 +94,15 @@ public class EXRWriter implements FrameConsumer<BitmapFrame> {
|
|||||||
bgrChannels[(i + 3) % 4] = channel;
|
bgrChannels[(i + 3) % 4] = channel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int alphaMask = keepAlpha ? 0 : 0xff;
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
for (FloatBuffer channel : bgrChannels) {
|
bgrChannels[0].put(((int) bgra.get() & 0xff) / 255f); // b
|
||||||
channel.put(((int) bgra.get() & 0xff) / 255f);
|
bgrChannels[1].put(((int) bgra.get() & 0xff) / 255f); // g
|
||||||
}
|
bgrChannels[2].put(((int) bgra.get() & 0xff) / 255f); // r
|
||||||
|
bgrChannels[3].put(((int) bgra.get() & 0xff | alphaMask) / 255f); // a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (depthFrame != null && depthChannel != null) {
|
if (depthFrame != null && depthChannel != null) {
|
||||||
|
|||||||
@@ -19,9 +19,11 @@ import java.util.Map;
|
|||||||
public class PNGWriter implements FrameConsumer<BitmapFrame> {
|
public class PNGWriter implements FrameConsumer<BitmapFrame> {
|
||||||
|
|
||||||
private final Path outputFolder;
|
private final Path outputFolder;
|
||||||
|
private final boolean keepAlpha;
|
||||||
|
|
||||||
public PNGWriter(Path outputFolder) throws IOException {
|
public PNGWriter(Path outputFolder, boolean keepAlpha) throws IOException {
|
||||||
this.outputFolder = outputFolder;
|
this.outputFolder = outputFolder;
|
||||||
|
this.keepAlpha = keepAlpha;
|
||||||
|
|
||||||
Files.createDirectories(outputFolder);
|
Files.createDirectories(outputFolder);
|
||||||
}
|
}
|
||||||
@@ -47,6 +49,7 @@ public class PNGWriter implements FrameConsumer<BitmapFrame> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void withImage(BitmapFrame frame, IOConsumer<Image> consumer) throws IOException {
|
private void withImage(BitmapFrame frame, IOConsumer<Image> consumer) throws IOException {
|
||||||
|
byte alphaMask = (byte) (keepAlpha ? 0 : 0xff);
|
||||||
ByteBuffer buffer = frame.getByteBuffer();
|
ByteBuffer buffer = frame.getByteBuffer();
|
||||||
ReadableDimension size = frame.getSize();
|
ReadableDimension size = frame.getSize();
|
||||||
int width = size.getWidth();
|
int width = size.getWidth();
|
||||||
@@ -58,7 +61,7 @@ public class PNGWriter implements FrameConsumer<BitmapFrame> {
|
|||||||
byte g = buffer.get();
|
byte g = buffer.get();
|
||||||
byte r = buffer.get();
|
byte r = buffer.get();
|
||||||
byte a = buffer.get();
|
byte a = buffer.get();
|
||||||
image.setRGBA(x, y, r, g, b, a);
|
image.setRGBA(x, y, r, g, b, a | alphaMask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
consumer.accept(image);
|
consumer.accept(image);
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ public class RenderSettings {
|
|||||||
private final File outputFile;
|
private final File outputFile;
|
||||||
|
|
||||||
private final boolean renderNameTags;
|
private final boolean renderNameTags;
|
||||||
|
private final boolean includeAlphaChannel;
|
||||||
private final boolean stabilizeYaw;
|
private final boolean stabilizeYaw;
|
||||||
private final boolean stabilizePitch;
|
private final boolean stabilizePitch;
|
||||||
private final boolean stabilizeRoll;
|
private final boolean stabilizeRoll;
|
||||||
@@ -187,6 +188,7 @@ public class RenderSettings {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
360,
|
360,
|
||||||
180,
|
180,
|
||||||
@@ -209,6 +211,7 @@ public class RenderSettings {
|
|||||||
int bitRate,
|
int bitRate,
|
||||||
File outputFile,
|
File outputFile,
|
||||||
boolean renderNameTags,
|
boolean renderNameTags,
|
||||||
|
boolean includeAlphaChannel,
|
||||||
boolean stabilizeYaw,
|
boolean stabilizeYaw,
|
||||||
boolean stabilizePitch,
|
boolean stabilizePitch,
|
||||||
boolean stabilizeRoll,
|
boolean stabilizeRoll,
|
||||||
@@ -231,6 +234,7 @@ public class RenderSettings {
|
|||||||
this.bitRate = bitRate;
|
this.bitRate = bitRate;
|
||||||
this.outputFile = outputFile;
|
this.outputFile = outputFile;
|
||||||
this.renderNameTags = renderNameTags;
|
this.renderNameTags = renderNameTags;
|
||||||
|
this.includeAlphaChannel = includeAlphaChannel;
|
||||||
this.stabilizeYaw = stabilizeYaw;
|
this.stabilizeYaw = stabilizeYaw;
|
||||||
this.stabilizePitch = stabilizePitch;
|
this.stabilizePitch = stabilizePitch;
|
||||||
this.stabilizeRoll = stabilizeRoll;
|
this.stabilizeRoll = stabilizeRoll;
|
||||||
@@ -256,6 +260,7 @@ public class RenderSettings {
|
|||||||
bitRate,
|
bitRate,
|
||||||
outputFile,
|
outputFile,
|
||||||
renderNameTags,
|
renderNameTags,
|
||||||
|
includeAlphaChannel,
|
||||||
stabilizeYaw,
|
stabilizeYaw,
|
||||||
stabilizePitch,
|
stabilizePitch,
|
||||||
stabilizeRoll,
|
stabilizeRoll,
|
||||||
@@ -415,6 +420,10 @@ public class RenderSettings {
|
|||||||
return renderNameTags;
|
return renderNameTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isIncludeAlphaChannel() {
|
||||||
|
return includeAlphaChannel;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isStabilizeYaw() {
|
public boolean isStabilizeYaw() {
|
||||||
return stabilizeYaw;
|
return stabilizeYaw;
|
||||||
}
|
}
|
||||||
@@ -478,6 +487,7 @@ public class RenderSettings {
|
|||||||
", bitRate=" + bitRate +
|
", bitRate=" + bitRate +
|
||||||
", outputFile=" + outputFile +
|
", outputFile=" + outputFile +
|
||||||
", renderNameTags=" + renderNameTags +
|
", renderNameTags=" + renderNameTags +
|
||||||
|
", includeAlphaChannel=" + includeAlphaChannel +
|
||||||
", stabilizeYaw=" + stabilizeYaw +
|
", stabilizeYaw=" + stabilizeYaw +
|
||||||
", stabilizePitch=" + stabilizePitch +
|
", stabilizePitch=" + stabilizePitch +
|
||||||
", stabilizeRoll=" + stabilizeRoll +
|
", stabilizeRoll=" + stabilizeRoll +
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
//#if MC>=11600
|
//#if MC>=11600
|
||||||
package com.replaymod.render.capturer;
|
package com.replaymod.render.capturer;
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.GlStateManager;
|
|
||||||
import com.replaymod.render.RenderSettings;
|
import com.replaymod.render.RenderSettings;
|
||||||
import com.replaymod.render.frame.CubicOpenGlFrame;
|
import com.replaymod.render.frame.CubicOpenGlFrame;
|
||||||
import com.replaymod.render.frame.ODSOpenGlFrame;
|
import com.replaymod.render.frame.ODSOpenGlFrame;
|
||||||
@@ -16,18 +15,13 @@ import java.io.IOException;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.replaymod.core.versions.MCVer.popMatrix;
|
|
||||||
import static com.replaymod.core.versions.MCVer.pushMatrix;
|
|
||||||
import static com.replaymod.core.versions.MCVer.resizeMainWindow;
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
|
|
||||||
|
|
||||||
public class IrisODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
public class IrisODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
||||||
|
|
||||||
public static final String SHADER_PACK_NAME = "assets/replaymod/iris/ods";
|
public static final String SHADER_PACK_NAME = "assets/replaymod/iris/ods";
|
||||||
public static IrisODSFrameCapturer INSTANCE;
|
public static IrisODSFrameCapturer INSTANCE;
|
||||||
private final CubicPboOpenGlFrameCapturer left, right;
|
private final CubicPboOpenGlFrameCapturer left, right;
|
||||||
private final String prevShaderPack;
|
private final String prevShaderPack;
|
||||||
|
private final boolean prevShadersEnabled;
|
||||||
private int direction;
|
private int direction;
|
||||||
private boolean isLeftEye;
|
private boolean isLeftEye;
|
||||||
|
|
||||||
@@ -67,13 +61,16 @@ public class IrisODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
|||||||
right = new CubicStereoFrameCapturer(worldRenderer, fakeInfo, frameSize);
|
right = new CubicStereoFrameCapturer(worldRenderer, fakeInfo, frameSize);
|
||||||
|
|
||||||
INSTANCE = this;
|
INSTANCE = this;
|
||||||
prevShaderPack = Iris.getIrisConfig().getShaderPackName().orElse(null);
|
IrisConfig irisConfig = Iris.getIrisConfig();
|
||||||
setShaderPack(SHADER_PACK_NAME);
|
prevShaderPack = irisConfig.getShaderPackName().orElse(null);
|
||||||
|
prevShadersEnabled = irisConfig.areShadersEnabled();
|
||||||
|
setShaderPack(SHADER_PACK_NAME, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setShaderPack(String name) {
|
private static void setShaderPack(String name, boolean enabled) {
|
||||||
IrisConfig irisConfig = Iris.getIrisConfig();
|
IrisConfig irisConfig = Iris.getIrisConfig();
|
||||||
irisConfig.setShaderPackName(name);
|
irisConfig.setShaderPackName(name);
|
||||||
|
irisConfig.setShadersEnabled(enabled);
|
||||||
try {
|
try {
|
||||||
irisConfig.save();
|
irisConfig.save();
|
||||||
Iris.reload();
|
Iris.reload();
|
||||||
@@ -121,7 +118,7 @@ public class IrisODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
|||||||
left.close();
|
left.close();
|
||||||
right.close();
|
right.close();
|
||||||
INSTANCE = null;
|
INSTANCE = null;
|
||||||
setShaderPack(prevShaderPack);
|
setShaderPack(prevShaderPack, prevShadersEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer {
|
private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer {
|
||||||
@@ -131,25 +128,8 @@ public class IrisODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) {
|
protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) {
|
||||||
resizeMainWindow(mc, getFrameWidth(), getFrameHeight());
|
|
||||||
|
|
||||||
pushMatrix();
|
|
||||||
frameBuffer().beginWrite(true);
|
|
||||||
|
|
||||||
GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
|
|
||||||
//#if MC>=11400
|
|
||||||
, false
|
|
||||||
//#endif
|
|
||||||
);
|
|
||||||
GlStateManager.enableTexture();
|
|
||||||
|
|
||||||
direction = captureData.ordinal();
|
direction = captureData.ordinal();
|
||||||
worldRenderer.renderWorld(partialTicks, null);
|
return super.renderFrame(frameId, partialTicks, captureData);
|
||||||
|
|
||||||
frameBuffer().endWrite();
|
|
||||||
popMatrix();
|
|
||||||
|
|
||||||
return captureFrame(frameId, captureData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.replaymod.render.capturer;
|
package com.replaymod.render.capturer;
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.GlStateManager;
|
|
||||||
import com.replaymod.render.rendering.Channel;
|
import com.replaymod.render.rendering.Channel;
|
||||||
import de.johni0702.minecraft.gui.utils.EventRegistrations;
|
import de.johni0702.minecraft.gui.utils.EventRegistrations;
|
||||||
import com.replaymod.render.RenderSettings;
|
import com.replaymod.render.RenderSettings;
|
||||||
@@ -20,12 +19,6 @@ import java.io.IOException;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.replaymod.core.versions.MCVer.popMatrix;
|
|
||||||
import static com.replaymod.core.versions.MCVer.pushMatrix;
|
|
||||||
import static com.replaymod.core.versions.MCVer.resizeMainWindow;
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
|
|
||||||
|
|
||||||
public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
||||||
private static final Identifier vertexResource = new Identifier("replaymod", "shader/ods.vert");
|
private static final Identifier vertexResource = new Identifier("replaymod", "shader/ods.vert");
|
||||||
private static final Identifier fragmentResource = new Identifier("replaymod", "shader/ods.frag");
|
private static final Identifier fragmentResource = new Identifier("replaymod", "shader/ods.frag");
|
||||||
@@ -166,25 +159,8 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) {
|
protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) {
|
||||||
resizeMainWindow(mc, getFrameWidth(), getFrameHeight());
|
|
||||||
|
|
||||||
pushMatrix();
|
|
||||||
frameBuffer().beginWrite(true);
|
|
||||||
|
|
||||||
GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
|
|
||||||
//#if MC>=11400
|
|
||||||
, false
|
|
||||||
//#endif
|
|
||||||
);
|
|
||||||
GlStateManager.enableTexture();
|
|
||||||
|
|
||||||
directionVariable.set(captureData.ordinal());
|
directionVariable.set(captureData.ordinal());
|
||||||
worldRenderer.renderWorld(partialTicks, null);
|
return super.renderFrame(frameId, partialTicks, captureData);
|
||||||
|
|
||||||
frameBuffer().endWrite();
|
|
||||||
popMatrix();
|
|
||||||
|
|
||||||
return captureFrame(frameId, captureData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ public class GuiExportFailed extends GuiScreen {
|
|||||||
oldSettings.getBitRate(),
|
oldSettings.getBitRate(),
|
||||||
oldSettings.getOutputFile(),
|
oldSettings.getOutputFile(),
|
||||||
oldSettings.isRenderNameTags(),
|
oldSettings.isRenderNameTags(),
|
||||||
|
oldSettings.isIncludeAlphaChannel(),
|
||||||
oldSettings.isStabilizeYaw(),
|
oldSettings.isStabilizeYaw(),
|
||||||
oldSettings.isStabilizePitch(),
|
oldSettings.isStabilizePitch(),
|
||||||
oldSettings.isStabilizeRoll(),
|
oldSettings.isStabilizeRoll(),
|
||||||
|
|||||||
48
src/main/java/com/replaymod/render/gui/GuiNoFfmpeg.java
Normal file
48
src/main/java/com/replaymod/render/gui/GuiNoFfmpeg.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package com.replaymod.render.gui;
|
||||||
|
|
||||||
|
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||||
|
import de.johni0702.minecraft.gui.container.GuiScreen;
|
||||||
|
import de.johni0702.minecraft.gui.element.GuiButton;
|
||||||
|
import de.johni0702.minecraft.gui.element.GuiLabel;
|
||||||
|
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||||
|
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import static com.replaymod.core.versions.MCVer.openURL;
|
||||||
|
import static de.johni0702.minecraft.gui.versions.MCVer.setClipboardString;
|
||||||
|
|
||||||
|
public class GuiNoFfmpeg extends GuiScreen {
|
||||||
|
|
||||||
|
private static final String LINK = "https://www.replaymod.com/docs/#installing-ffmpeg";
|
||||||
|
|
||||||
|
private final GuiLabel message = new GuiLabel()
|
||||||
|
.setI18nText("replaymod.gui.rendering.error.message");
|
||||||
|
private final GuiLabel link = new GuiLabel()
|
||||||
|
.setText(LINK);
|
||||||
|
private final GuiButton openLinkButton = new GuiButton()
|
||||||
|
.setI18nLabel("chat.link.open")
|
||||||
|
.setSize(100, 20)
|
||||||
|
.onClick(() -> openURL(URI.create(LINK)));
|
||||||
|
private final GuiButton copyToClipboardButton = new GuiButton()
|
||||||
|
.setI18nLabel("chat.copy")
|
||||||
|
.setSize(100, 20)
|
||||||
|
.onClick(() -> setClipboardString(LINK));
|
||||||
|
private final GuiButton backButton = new GuiButton()
|
||||||
|
.setI18nLabel("gui.back")
|
||||||
|
.setSize(100, 20);
|
||||||
|
private final GuiPanel buttons = new GuiPanel()
|
||||||
|
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER).setSpacing(4))
|
||||||
|
.addElements(null, openLinkButton, copyToClipboardButton, backButton);
|
||||||
|
|
||||||
|
{
|
||||||
|
setBackground(Background.DIRT);
|
||||||
|
setTitle(new GuiLabel().setI18nText("replaymod.gui.rendering.error.title"));
|
||||||
|
setLayout(new VerticalLayout(VerticalLayout.Alignment.CENTER).setSpacing(30));
|
||||||
|
addElements(new VerticalLayout.Data(0.5), message, link, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GuiNoFfmpeg(Runnable goBack) {
|
||||||
|
backButton.onClick(goBack);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,6 @@ import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
|
|||||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
||||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadablePoint;
|
import de.johni0702.minecraft.gui.utils.lwjgl.ReadablePoint;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.gui.screen.NoticeScreen;
|
|
||||||
import net.minecraft.util.crash.CrashReport;
|
import net.minecraft.util.crash.CrashReport;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
@@ -181,17 +180,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> implements
|
|||||||
videoRenderer.renderVideo();
|
videoRenderer.renderVideo();
|
||||||
} catch (FFmpegWriter.NoFFmpegException e) {
|
} catch (FFmpegWriter.NoFFmpegException e) {
|
||||||
LOGGER.error("Rendering video:", e);
|
LOGGER.error("Rendering video:", e);
|
||||||
NoticeScreen errorScreen = new NoticeScreen(
|
mc.openScreen(new GuiNoFfmpeg(container::display).toMinecraft());
|
||||||
//#if MC>=11400
|
|
||||||
container::display,
|
|
||||||
new TranslatableText("replaymod.gui.rendering.error.title"),
|
|
||||||
new TranslatableText("replaymod.gui.rendering.error.message")
|
|
||||||
//#else
|
|
||||||
//$$ I18n.format("replaymod.gui.rendering.error.title"),
|
|
||||||
//$$ I18n.format("replaymod.gui.rendering.error.message")
|
|
||||||
//#endif
|
|
||||||
);
|
|
||||||
mc.openScreen(errorScreen);
|
|
||||||
return;
|
return;
|
||||||
} catch (FFmpegWriter.FFmpegStartupException e) {
|
} catch (FFmpegWriter.FFmpegStartupException e) {
|
||||||
int jobsToSkip = jobsDone;
|
int jobsToSkip = jobsDone;
|
||||||
@@ -229,7 +218,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> implements
|
|||||||
ReplayFile replayFile = null;
|
ReplayFile replayFile = null;
|
||||||
try {
|
try {
|
||||||
replayFile = mod.getCore().files.open(next.getKey().toPath());
|
replayFile = mod.getCore().files.open(next.getKey().toPath());
|
||||||
replayHandler = mod.startReplay(replayFile, true, false);
|
replayHandler = mod.startReplay(replayFile, false, false);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Utils.error(LOGGER, container, CrashReport.create(e, "Opening replay"), () -> {});
|
Utils.error(LOGGER, container, CrashReport.create(e, "Opening replay"), () -> {});
|
||||||
container.display(); // Re-show the queue popup and the new error popup
|
container.display(); // Re-show the queue popup and the new error popup
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import de.johni0702.minecraft.gui.utils.Utils;
|
|||||||
import de.johni0702.minecraft.gui.utils.lwjgl.Color;
|
import de.johni0702.minecraft.gui.utils.lwjgl.Color;
|
||||||
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
|
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
|
||||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
||||||
import net.minecraft.client.gui.screen.NoticeScreen;
|
|
||||||
import net.minecraft.client.resource.language.I18n;
|
import net.minecraft.client.resource.language.I18n;
|
||||||
import net.minecraft.util.crash.CrashReport;
|
import net.minecraft.util.crash.CrashReport;
|
||||||
|
|
||||||
@@ -149,6 +148,9 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
|
|||||||
public final GuiCheckbox nametagCheckbox = new GuiCheckbox()
|
public final GuiCheckbox nametagCheckbox = new GuiCheckbox()
|
||||||
.setI18nLabel("replaymod.gui.rendersettings.nametags");
|
.setI18nLabel("replaymod.gui.rendersettings.nametags");
|
||||||
|
|
||||||
|
public final GuiCheckbox alphaCheckbox = new GuiCheckbox()
|
||||||
|
.setI18nLabel("replaymod.gui.rendersettings.includealpha");
|
||||||
|
|
||||||
public final GuiPanel stabilizePanel = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(10));
|
public final GuiPanel stabilizePanel = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(10));
|
||||||
public final GuiCheckbox stabilizeYaw = new GuiCheckbox(stabilizePanel)
|
public final GuiCheckbox stabilizeYaw = new GuiCheckbox(stabilizePanel)
|
||||||
.setI18nLabel("replaymod.gui.yaw");
|
.setI18nLabel("replaymod.gui.yaw");
|
||||||
@@ -188,7 +190,7 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
|
|||||||
.setSize(200, 20).setValues(RenderSettings.AntiAliasing.values()).setSelected(RenderSettings.AntiAliasing.NONE);
|
.setSize(200, 20).setValues(RenderSettings.AntiAliasing.values()).setSelected(RenderSettings.AntiAliasing.NONE);
|
||||||
|
|
||||||
public final GuiPanel advancedPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(15))
|
public final GuiPanel advancedPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(15))
|
||||||
.addElements(null, nametagCheckbox, new GuiPanel().setLayout(
|
.addElements(null, nametagCheckbox, alphaCheckbox, new GuiPanel().setLayout(
|
||||||
new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(15))
|
new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(15))
|
||||||
.addElements(new GridLayout.Data(0, 0.5),
|
.addElements(new GridLayout.Data(0, 0.5),
|
||||||
new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel,
|
new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel,
|
||||||
@@ -240,17 +242,7 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
|
|||||||
videoRenderer.renderVideo();
|
videoRenderer.renderVideo();
|
||||||
} catch (FFmpegWriter.NoFFmpegException e) {
|
} catch (FFmpegWriter.NoFFmpegException e) {
|
||||||
LOGGER.error("Rendering video:", e);
|
LOGGER.error("Rendering video:", e);
|
||||||
NoticeScreen errorScreen = new NoticeScreen(
|
getMinecraft().openScreen(new GuiNoFfmpeg(getScreen()::display).toMinecraft());
|
||||||
//#if MC>=11400
|
|
||||||
getScreen()::display,
|
|
||||||
new TranslatableText("replaymod.gui.rendering.error.title"),
|
|
||||||
new TranslatableText("replaymod.gui.rendering.error.message")
|
|
||||||
//#else
|
|
||||||
//$$ I18n.format("replaymod.gui.rendering.error.title"),
|
|
||||||
//$$ I18n.format("replaymod.gui.rendering.error.message")
|
|
||||||
//#endif
|
|
||||||
);
|
|
||||||
getMinecraft().openScreen(errorScreen);
|
|
||||||
} catch (FFmpegWriter.FFmpegStartupException e) {
|
} catch (FFmpegWriter.FFmpegStartupException e) {
|
||||||
GuiExportFailed.tryToRecover(e, newSettings -> {
|
GuiExportFailed.tryToRecover(e, newSettings -> {
|
||||||
// Update settings with fixed ffmpeg arguments
|
// Update settings with fixed ffmpeg arguments
|
||||||
@@ -533,6 +525,7 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
|
|||||||
}
|
}
|
||||||
outputFileButton.setLabel(this.outputFile.getName());
|
outputFileButton.setLabel(this.outputFile.getName());
|
||||||
nametagCheckbox.setChecked(settings.isRenderNameTags());
|
nametagCheckbox.setChecked(settings.isRenderNameTags());
|
||||||
|
alphaCheckbox.setChecked(settings.isIncludeAlphaChannel());
|
||||||
stabilizeYaw.setChecked(settings.isStabilizeYaw());
|
stabilizeYaw.setChecked(settings.isStabilizeYaw());
|
||||||
stabilizePitch.setChecked(settings.isStabilizePitch());
|
stabilizePitch.setChecked(settings.isStabilizePitch());
|
||||||
stabilizeRoll.setChecked(settings.isStabilizeRoll());
|
stabilizeRoll.setChecked(settings.isStabilizeRoll());
|
||||||
@@ -571,6 +564,7 @@ public class GuiRenderSettings extends AbstractGuiPopup<GuiRenderSettings> {
|
|||||||
bitRateField.getInteger() << (10 * bitRateUnit.getSelected()),
|
bitRateField.getInteger() << (10 * bitRateUnit.getSelected()),
|
||||||
serialize && !userDefinedOutputFileName ? getParentFile(outputFile) : outputFile,
|
serialize && !userDefinedOutputFileName ? getParentFile(outputFile) : outputFile,
|
||||||
nametagCheckbox.isChecked(),
|
nametagCheckbox.isChecked(),
|
||||||
|
alphaCheckbox.isChecked(),
|
||||||
stabilizeYaw.isChecked() && (serialize || stabilizeYaw.isEnabled()),
|
stabilizeYaw.isChecked() && (serialize || stabilizeYaw.isEnabled()),
|
||||||
stabilizePitch.isChecked() && (serialize || stabilizePitch.isEnabled()),
|
stabilizePitch.isChecked() && (serialize || stabilizePitch.isEnabled()),
|
||||||
stabilizeRoll.isChecked() && (serialize || stabilizeRoll.isEnabled()),
|
stabilizeRoll.isChecked() && (serialize || stabilizeRoll.isEnabled()),
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ public class GuiVideoRenderer extends GuiScreen implements Tickable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
long current = System.currentTimeMillis();
|
long current = System.nanoTime() / 1_000_000;
|
||||||
|
|
||||||
//first, update the total render time (only if rendering is not paused and has already started)
|
//first, update the total render time (only if rendering is not paused and has already started)
|
||||||
if(!renderer.isPaused() && renderer.getFramesDone() > 0 && prevTime > -1) {
|
if(!renderer.isPaused() && renderer.getFramesDone() > 0 && prevTime > -1) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.replaymod.render.Setting;
|
|||||||
import com.replaymod.render.capturer.CaptureData;
|
import com.replaymod.render.capturer.CaptureData;
|
||||||
import com.replaymod.render.capturer.RenderInfo;
|
import com.replaymod.render.capturer.RenderInfo;
|
||||||
import com.replaymod.render.capturer.WorldRenderer;
|
import com.replaymod.render.capturer.WorldRenderer;
|
||||||
|
import com.replaymod.render.mixin.GameRendererAccessor;
|
||||||
import com.replaymod.replay.ReplayModReplay;
|
import com.replaymod.replay.ReplayModReplay;
|
||||||
import de.johni0702.minecraft.gui.utils.EventRegistrations;
|
import de.johni0702.minecraft.gui.utils.EventRegistrations;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
@@ -81,11 +82,16 @@ public class EntityRendererHandler extends EventRegistrations implements WorldRe
|
|||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
if (mc.world != null && mc.player != null) {
|
if (mc.world != null && mc.player != null) {
|
||||||
|
GameRendererAccessor gameRenderer = (GameRendererAccessor) mc.gameRenderer;
|
||||||
Screen orgScreen = mc.currentScreen;
|
Screen orgScreen = mc.currentScreen;
|
||||||
boolean orgPauseOnLostFocus = mc.options.pauseOnLostFocus;
|
boolean orgPauseOnLostFocus = mc.options.pauseOnLostFocus;
|
||||||
|
boolean orgRenderHand = gameRenderer.getRenderHand();
|
||||||
try {
|
try {
|
||||||
mc.currentScreen = null; // do not want to render the current screen (that'd just be the progress gui)
|
mc.currentScreen = null; // do not want to render the current screen (that'd just be the progress gui)
|
||||||
mc.options.pauseOnLostFocus = false; // do not want the pause menu to open if the window is unfocused
|
mc.options.pauseOnLostFocus = false; // do not want the pause menu to open if the window is unfocused
|
||||||
|
if (omnidirectional) {
|
||||||
|
gameRenderer.setRenderHand(false); // makes no sense, we wouldn't even know where to put it
|
||||||
|
}
|
||||||
|
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
mc.gameRenderer.render(partialTicks, finishTimeNano, true);
|
mc.gameRenderer.render(partialTicks, finishTimeNano, true);
|
||||||
@@ -100,6 +106,7 @@ public class EntityRendererHandler extends EventRegistrations implements WorldRe
|
|||||||
} finally {
|
} finally {
|
||||||
mc.currentScreen = orgScreen;
|
mc.currentScreen = orgScreen;
|
||||||
mc.options.pauseOnLostFocus = orgPauseOnLostFocus;
|
mc.options.pauseOnLostFocus = orgPauseOnLostFocus;
|
||||||
|
gameRenderer.setRenderHand(orgRenderHand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.replaymod.render.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.render.GameRenderer;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(GameRenderer.class)
|
||||||
|
public interface GameRendererAccessor {
|
||||||
|
@Accessor
|
||||||
|
boolean getRenderHand();
|
||||||
|
@Accessor
|
||||||
|
void setRenderHand(boolean value);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.replaymod.render.mixin;
|
|||||||
import net.minecraft.client.util.Window;
|
import net.minecraft.client.util.Window;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||||
|
|
||||||
@Mixin(Window.class)
|
@Mixin(Window.class)
|
||||||
public interface MainWindowAccessor {
|
public interface MainWindowAccessor {
|
||||||
@@ -14,4 +15,12 @@ public interface MainWindowAccessor {
|
|||||||
int getFramebufferHeight();
|
int getFramebufferHeight();
|
||||||
@Accessor
|
@Accessor
|
||||||
void setFramebufferHeight(int value);
|
void setFramebufferHeight(int value);
|
||||||
|
// FIXME preprocessor should be able to infer this mapping
|
||||||
|
// FIXME preprocessor should be able to remap this one when the mapping is given manually
|
||||||
|
//#if MC>=11500
|
||||||
|
@Invoker
|
||||||
|
//#else
|
||||||
|
//$$ @Invoker("method_4483")
|
||||||
|
//#endif
|
||||||
|
void invokeUpdateFramebufferSize();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ public abstract class Mixin_ChromaKeyColorSky {
|
|||||||
@Shadow @Final private MinecraftClient client;
|
@Shadow @Final private MinecraftClient client;
|
||||||
|
|
||||||
//#if MC>=11800
|
//#if MC>=11800
|
||||||
//$$ @Inject(method = "renderSky(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/util/math/Matrix4f;FLjava/lang/Runnable;)V",
|
//$$ @Inject(
|
||||||
|
//#if MC>=11802
|
||||||
|
//$$ method = "renderSky(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/util/math/Matrix4f;FLnet/minecraft/client/render/Camera;ZLjava/lang/Runnable;)V",
|
||||||
|
//#else
|
||||||
|
//$$ method = "renderSky(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/util/math/Matrix4f;FLjava/lang/Runnable;)V",
|
||||||
|
//#endif
|
||||||
//$$ at = @At(value = "INVOKE", target = "Ljava/lang/Runnable;run()V", remap = false, shift = At.Shift.AFTER),
|
//$$ at = @At(value = "INVOKE", target = "Ljava/lang/Runnable;run()V", remap = false, shift = At.Shift.AFTER),
|
||||||
//$$ cancellable = true)
|
//$$ cancellable = true)
|
||||||
//#elseif MC>=11400 || 10710>=MC
|
//#elseif MC>=11400 || 10710>=MC
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package com.replaymod.render.mixin;
|
|||||||
import com.replaymod.render.capturer.IrisODSFrameCapturer;
|
import com.replaymod.render.capturer.IrisODSFrameCapturer;
|
||||||
import net.coderbot.iris.Iris;
|
import net.coderbot.iris.Iris;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import org.objectweb.asm.Opcodes;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Pseudo;
|
import org.spongepowered.asm.mixin.Pseudo;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
@@ -15,14 +14,14 @@ import java.nio.file.Path;
|
|||||||
@Pseudo
|
@Pseudo
|
||||||
@Mixin(value = Iris.class, remap = false)
|
@Mixin(value = Iris.class, remap = false)
|
||||||
public class Mixin_LoadIrisOdsShaderPack {
|
public class Mixin_LoadIrisOdsShaderPack {
|
||||||
@Redirect(method = "loadExternalShaderpack", at = @At(value = "FIELD", opcode = Opcodes.GETSTATIC, target = "Lnet/coderbot/iris/Iris;SHADERPACKS_DIRECTORY:Ljava/nio/file/Path;"))
|
@Redirect(method = "loadExternalShaderpack", at = @At(value = "INVOKE", target = "Lnet/coderbot/iris/Iris;getShaderpacksDirectory()Ljava/nio/file/Path;"))
|
||||||
private static Path loadReplayModOdsPack(String name) {
|
private static Path loadReplayModOdsPack(String name) {
|
||||||
if (IrisODSFrameCapturer.INSTANCE != null && IrisODSFrameCapturer.SHADER_PACK_NAME.equals(name)) {
|
if (IrisODSFrameCapturer.INSTANCE != null && IrisODSFrameCapturer.SHADER_PACK_NAME.equals(name)) {
|
||||||
return FabricLoader.getInstance().getModContainer("replaymod")
|
return FabricLoader.getInstance().getModContainer("replaymod")
|
||||||
.orElseThrow(() -> new RuntimeException("Failed to get mod container for ReplayMod"))
|
.orElseThrow(() -> new RuntimeException("Failed to get mod container for ReplayMod"))
|
||||||
.getRootPath();
|
.getRootPath();
|
||||||
} else {
|
} else {
|
||||||
return Iris.SHADERPACKS_DIRECTORY;
|
return Iris.getShaderpacksDirectory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
|||||||
|
|
||||||
@Mixin(GameRenderer.class)
|
@Mixin(GameRenderer.class)
|
||||||
public abstract class Mixin_Omnidirectional_Camera implements EntityRendererHandler.IEntityRenderer {
|
public abstract class Mixin_Omnidirectional_Camera implements EntityRendererHandler.IEntityRenderer {
|
||||||
@Redirect(method = "method_22973", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/Matrix4f;viewboxMatrix(DFFF)Lnet/minecraft/util/math/Matrix4f;"))
|
@Redirect(method = "getBasicProjectionMatrix", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/Matrix4f;viewboxMatrix(DFFF)Lnet/minecraft/util/math/Matrix4f;"))
|
||||||
private Matrix4f replayModRender_perspective$0(double fovY, float aspect, float zNear, float zFar) {
|
private Matrix4f replayModRender_perspective$0(double fovY, float aspect, float zNear, float zFar) {
|
||||||
return replayModRender_perspective((float) fovY, aspect, zNear, zFar);
|
return replayModRender_perspective((float) fovY, aspect, zNear, zFar);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package com.replaymod.render.mixin;
|
|
||||||
|
|
||||||
import com.replaymod.core.versions.MCVer;
|
|
||||||
import com.replaymod.render.hooks.EntityRendererHandler;
|
|
||||||
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.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
//#if MC>=10800
|
|
||||||
import net.minecraft.client.render.Frustum;
|
|
||||||
//#else
|
|
||||||
//$$ import net.minecraft.client.renderer.culling.Frustrum;
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
//#if MC>=10800
|
|
||||||
@Mixin(Frustum.class)
|
|
||||||
//#else
|
|
||||||
//$$ @Mixin(Frustrum.class)
|
|
||||||
//#endif
|
|
||||||
public abstract class Mixin_Omnidirectional_DisableFrustumCulling {
|
|
||||||
//#if MC>=11500
|
|
||||||
@Inject(method = "isAnyCornerVisible", at = @At("HEAD"), cancellable = true)
|
|
||||||
//#else
|
|
||||||
//$$ @Inject(method = "intersects", at = @At("HEAD"), cancellable = true)
|
|
||||||
//#endif
|
|
||||||
public void intersects(CallbackInfoReturnable<Boolean> ci) {
|
|
||||||
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) MCVer.getMinecraft().gameRenderer).replayModRender_getHandler();
|
|
||||||
if (handler != null && handler.omnidirectional) {
|
|
||||||
// Note the following used to be true but for simplicity non-ODS omnidirectional is the same now too.
|
|
||||||
// Normally the camera is always facing the direction of the omnidirectional image face that is currently
|
|
||||||
// getting rendered. With ODS however, the camera is always facing forwards and the turning happens in the
|
|
||||||
// vertex shader (non-trivial due to stereo). As such, all chunks need to be rendered all the time for ODS.
|
|
||||||
ci.setReturnValue(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package com.replaymod.render.mixin;
|
|
||||||
|
|
||||||
import com.replaymod.render.hooks.EntityRendererHandler;
|
|
||||||
import net.minecraft.client.render.Camera;
|
|
||||||
import net.minecraft.client.render.GameRenderer;
|
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
|
||||||
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(GameRenderer.class)
|
|
||||||
public abstract class Mixin_Omnidirectional_SkipHand implements EntityRendererHandler.IEntityRenderer {
|
|
||||||
@Inject(method = "renderHand", at = @At("HEAD"), cancellable = true)
|
|
||||||
private void replayModRender_renderSpectatorHand(
|
|
||||||
//#if MC>=11500
|
|
||||||
MatrixStack matrixStack,
|
|
||||||
//#endif
|
|
||||||
//#if MC>=11400
|
|
||||||
Camera camera,
|
|
||||||
//#endif
|
|
||||||
float partialTicks,
|
|
||||||
//#if MC<11400
|
|
||||||
//$$ int renderPass,
|
|
||||||
//#endif
|
|
||||||
CallbackInfo ci
|
|
||||||
) {
|
|
||||||
EntityRendererHandler handler = replayModRender_getHandler();
|
|
||||||
if (handler != null && handler.omnidirectional) {
|
|
||||||
// No spectator hands during 360° view, we wouldn't even know where to put it
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|||||||
|
|
||||||
@Mixin(GameRenderer.class)
|
@Mixin(GameRenderer.class)
|
||||||
public abstract class Mixin_Stereoscopic_Camera implements EntityRendererHandler.IEntityRenderer {
|
public abstract class Mixin_Stereoscopic_Camera implements EntityRendererHandler.IEntityRenderer {
|
||||||
@Inject(method = "method_22973", at = @At("RETURN"), cancellable = true)
|
@Inject(method = "getBasicProjectionMatrix", at = @At("RETURN"), cancellable = true)
|
||||||
private void replayModRender_setupStereoscopicProjection(CallbackInfoReturnable<Matrix4f> ci) {
|
private void replayModRender_setupStereoscopicProjection(CallbackInfoReturnable<Matrix4f> ci) {
|
||||||
if (replayModRender_getHandler() != null) {
|
if (replayModRender_getHandler() != null) {
|
||||||
Matrix4f offset;
|
Matrix4f offset;
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
|
|
||||||
private Framebuffer guiFramebuffer;
|
private Framebuffer guiFramebuffer;
|
||||||
private int displayWidth, displayHeight;
|
private int displayWidth, displayHeight;
|
||||||
|
private int framebufferWidth, framebufferHeight;
|
||||||
|
|
||||||
public VideoRenderer(RenderSettings settings, ReplayHandler replayHandler, Timeline timeline) throws IOException {
|
public VideoRenderer(RenderSettings settings, ReplayHandler replayHandler, Timeline timeline) throws IOException {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
@@ -135,12 +136,12 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
FrameConsumer<BitmapFrame> frameConsumer;
|
FrameConsumer<BitmapFrame> frameConsumer;
|
||||||
if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.EXR) {
|
if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.EXR) {
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
frameConsumer = new EXRWriter(settings.getOutputFile().toPath());
|
frameConsumer = new EXRWriter(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel());
|
||||||
//#else
|
//#else
|
||||||
//$$ throw new UnsupportedOperationException("EXR requires LWJGL3");
|
//$$ throw new UnsupportedOperationException("EXR requires LWJGL3");
|
||||||
//#endif
|
//#endif
|
||||||
} else if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.PNG) {
|
} else if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.PNG) {
|
||||||
frameConsumer = new PNGWriter(settings.getOutputFile().toPath());
|
frameConsumer = new PNGWriter(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel());
|
||||||
} else {
|
} else {
|
||||||
frameConsumer = new FFmpegWriter(this);
|
frameConsumer = new FFmpegWriter(this);
|
||||||
}
|
}
|
||||||
@@ -245,10 +246,10 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
public float updateForNextFrame() {
|
public float updateForNextFrame() {
|
||||||
// because the jGui lib uses Minecraft's displayWidth and displayHeight values, update these temporarily
|
// because the jGui lib uses Minecraft's displayWidth and displayHeight values, update these temporarily
|
||||||
MainWindowAccessor acc = (MainWindowAccessor) (Object) mc.getWindow();
|
MainWindowAccessor acc = (MainWindowAccessor) (Object) mc.getWindow();
|
||||||
int displayWidthBefore = acc.getFramebufferWidth();
|
int framebufferWidthBefore = acc.getFramebufferWidth();
|
||||||
int displayHeightBefore = acc.getFramebufferHeight();
|
int framebufferHeightBefore = acc.getFramebufferHeight();
|
||||||
acc.setFramebufferWidth(displayWidth);
|
acc.setFramebufferWidth(framebufferWidth);
|
||||||
acc.setFramebufferHeight(displayHeight);
|
acc.setFramebufferHeight(framebufferHeight);
|
||||||
|
|
||||||
if (!settings.isHighPerformance() || framesDone % fps == 0) {
|
if (!settings.isHighPerformance() || framesDone % fps == 0) {
|
||||||
while (drawGui() && paused) {
|
while (drawGui() && paused) {
|
||||||
@@ -289,8 +290,8 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// change Minecraft's display size back
|
// change Minecraft's display size back
|
||||||
acc.setFramebufferWidth(displayWidthBefore);
|
acc.setFramebufferWidth(framebufferWidthBefore);
|
||||||
acc.setFramebufferHeight(displayHeightBefore);
|
acc.setFramebufferHeight(framebufferHeightBefore);
|
||||||
|
|
||||||
if (cameraPathExporter != null) {
|
if (cameraPathExporter != null) {
|
||||||
cameraPathExporter.recordFrame(timer.tickDelta);
|
cameraPathExporter.recordFrame(timer.tickDelta);
|
||||||
@@ -362,6 +363,7 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateDisplaySize();
|
updateDisplaySize();
|
||||||
|
updateFramebufferSize();
|
||||||
|
|
||||||
gui.toMinecraft().init(mc, mc.getWindow().getScaledWidth(), mc.getWindow().getScaledHeight());
|
gui.toMinecraft().init(mc, mc.getWindow().getScaledWidth(), mc.getWindow().getScaledHeight());
|
||||||
|
|
||||||
@@ -369,9 +371,9 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
|
|
||||||
// Set up our own framebuffer to render the GUI to
|
// Set up our own framebuffer to render the GUI to
|
||||||
//#if MC>=11700
|
//#if MC>=11700
|
||||||
//$$ guiFramebuffer = new WindowFramebuffer(displayWidth, displayHeight);
|
//$$ guiFramebuffer = new WindowFramebuffer(framebufferWidth, framebufferHeight);
|
||||||
//#else
|
//#else
|
||||||
guiFramebuffer = new Framebuffer(displayWidth, displayHeight, true
|
guiFramebuffer = new Framebuffer(framebufferWidth, framebufferHeight, true
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
, false
|
, false
|
||||||
//#endif
|
//#endif
|
||||||
@@ -425,7 +427,7 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finally, resize the Minecraft framebuffer to the actual width/height of the window
|
// Finally, resize the Minecraft framebuffer to the actual width/height of the window
|
||||||
resizeMainWindow(mc, displayWidth, displayHeight);
|
resizeMainWindow(mc, framebufferWidth, framebufferHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeTaskQueue() {
|
private void executeTaskQueue() {
|
||||||
@@ -483,17 +485,23 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize the GUI framebuffer if the display size changed
|
// Check if display size has changes and force recalculate GUI framebuffer size.
|
||||||
if (displaySizeChanged()) {
|
if (displaySizeChanged()) {
|
||||||
updateDisplaySize();
|
updateDisplaySize();
|
||||||
|
((MainWindowAccessor) (Object) window).invokeUpdateFramebufferSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize the GUI framebuffer if the display size changed
|
||||||
|
if (framebufferSizeChanged()) {
|
||||||
|
updateFramebufferSize();
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
guiFramebuffer.resize(displayWidth, displayHeight
|
guiFramebuffer.resize(framebufferWidth, framebufferHeight
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
, false
|
, false
|
||||||
//#endif
|
//#endif
|
||||||
);
|
);
|
||||||
//#else
|
//#else
|
||||||
//$$ guiFramebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight);
|
//$$ guiFramebuffer.createBindFramebuffer(framebufferWidth, framebufferHeight);
|
||||||
//#endif
|
//#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,7 +602,7 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
guiFramebuffer.endWrite();
|
guiFramebuffer.endWrite();
|
||||||
popMatrix();
|
popMatrix();
|
||||||
pushMatrix();
|
pushMatrix();
|
||||||
guiFramebuffer.draw(displayWidth, displayHeight);
|
guiFramebuffer.draw(framebufferWidth, framebufferHeight);
|
||||||
popMatrix();
|
popMatrix();
|
||||||
|
|
||||||
//#if MC>=11500
|
//#if MC>=11500
|
||||||
@@ -633,12 +641,18 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
private boolean displaySizeChanged() {
|
private boolean displaySizeChanged() {
|
||||||
int realWidth = mc.getWindow().getWidth();
|
int realWidth = mc.getWindow().getWidth();
|
||||||
int realHeight = mc.getWindow().getHeight();
|
int realHeight = mc.getWindow().getHeight();
|
||||||
|
return displayWidth != realWidth || displayHeight != realHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean framebufferSizeChanged() {
|
||||||
|
int realWidth = mc.getWindow().getFramebufferWidth();
|
||||||
|
int realHeight = mc.getWindow().getFramebufferHeight();
|
||||||
if (realWidth == 0 || realHeight == 0) {
|
if (realWidth == 0 || realHeight == 0) {
|
||||||
// These can be zero on Windows if minimized.
|
// These can be zero on Windows if minimized.
|
||||||
// Creating zero-sized framebuffers however will throw an error, so we never want to switch to zero values.
|
// Creating zero-sized framebuffers however will throw an error, so we never want to switch to zero values.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return displayWidth != realWidth || displayHeight != realHeight;
|
return framebufferWidth != realWidth || framebufferHeight != realHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDisplaySize() {
|
private void updateDisplaySize() {
|
||||||
@@ -646,6 +660,11 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
displayHeight = mc.getWindow().getHeight();
|
displayHeight = mc.getWindow().getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateFramebufferSize() {
|
||||||
|
framebufferWidth = mc.getWindow().getFramebufferWidth();
|
||||||
|
framebufferHeight = mc.getWindow().getFramebufferHeight();
|
||||||
|
}
|
||||||
|
|
||||||
public int getFramesDone() {
|
public int getFramesDone() {
|
||||||
return framesDone;
|
return framesDone;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static com.replaymod.core.versions.MCVer.*;
|
import static com.replaymod.core.versions.MCVer.*;
|
||||||
import static com.replaymod.replaystudio.util.Utils.readInt;
|
import static com.replaymod.replaystudio.util.Utils.readInt;
|
||||||
@@ -349,6 +352,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
terminate = true;
|
terminate = true;
|
||||||
|
syncSender.shutdown();
|
||||||
events.unregister();
|
events.unregister();
|
||||||
try {
|
try {
|
||||||
channelInactive(ctx);
|
channelInactive(ctx);
|
||||||
@@ -403,53 +407,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
super.channelRead(ctx, p);
|
super.channelRead(ctx, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we do not give minecraft time to tick, there will be dead entity artifacts left in the world
|
maybeRemoveDeadEntities(p);
|
||||||
// Therefore we have to remove all loaded, dead entities manually if we are in sync mode.
|
|
||||||
// We do this after every SpawnX packet and after the destroy entities packet.
|
|
||||||
if (!asyncMode && mc.world != null) {
|
|
||||||
if (p instanceof PlayerSpawnS2CPacket
|
|
||||||
|| p instanceof EntitySpawnS2CPacket
|
|
||||||
|| p instanceof MobSpawnS2CPacket
|
|
||||||
//#if MC<11600
|
|
||||||
//$$ || p instanceof EntitySpawnGlobalS2CPacket
|
|
||||||
//#endif
|
|
||||||
|| p instanceof PaintingSpawnS2CPacket
|
|
||||||
|| p instanceof ExperienceOrbSpawnS2CPacket
|
|
||||||
|| p instanceof EntitiesDestroyS2CPacket) {
|
|
||||||
ClientWorld world = mc.world;
|
|
||||||
//#if MC>=11700
|
|
||||||
//$$ // From the looks of it, this has now been resolved (thanks to EntityChangeListener)
|
|
||||||
//#elseif MC>=11400
|
|
||||||
// Note: Not sure if it's still required but there's this really handy method anyway
|
|
||||||
world.finishRemovingEntities();
|
|
||||||
//#else
|
|
||||||
//$$ Iterator<Entity> iter = world.loadedEntityList.iterator();
|
|
||||||
//$$ while (iter.hasNext()) {
|
|
||||||
//$$ Entity entity = iter.next();
|
|
||||||
//$$ if (entity.isDead) {
|
|
||||||
//$$ int chunkX = entity.chunkCoordX;
|
|
||||||
//$$ int chunkY = entity.chunkCoordZ;
|
|
||||||
//$$
|
|
||||||
//#if MC>=11400
|
|
||||||
//$$ if (entity.addedToChunk && world.getChunkProvider().provideChunk(chunkX, chunkY, false, false) != null) {
|
|
||||||
//#else
|
|
||||||
//#if MC>=10904
|
|
||||||
//$$ if (entity.addedToChunk && world.getChunkProvider().getLoadedChunk(chunkX, chunkY) != null) {
|
|
||||||
//#else
|
|
||||||
//$$ if (entity.addedToChunk && world.getChunkProvider().chunkExists(chunkX, chunkY)) {
|
|
||||||
//#endif
|
|
||||||
//#endif
|
|
||||||
//$$ world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity);
|
|
||||||
//$$ }
|
|
||||||
//$$
|
|
||||||
//$$ iter.remove();
|
|
||||||
//$$ world.onEntityRemoved(entity);
|
|
||||||
//$$ }
|
|
||||||
//$$
|
|
||||||
//$$ }
|
|
||||||
//#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
if (p instanceof ChunkDataS2CPacket) {
|
if (p instanceof ChunkDataS2CPacket) {
|
||||||
@@ -504,6 +462,69 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we do not give minecraft time to tick, there will be dead entity artifacts left in the world
|
||||||
|
// Therefore we have to remove all loaded, dead entities manually if we are in sync mode.
|
||||||
|
// We do this after every SpawnX packet and after the destroy entities packet.
|
||||||
|
private void maybeRemoveDeadEntities(Packet packet) {
|
||||||
|
if (asyncMode) {
|
||||||
|
return; // MC should have enough time to tick
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean relevantPacket = packet instanceof PlayerSpawnS2CPacket
|
||||||
|
|| packet instanceof EntitySpawnS2CPacket
|
||||||
|
|| packet instanceof MobSpawnS2CPacket
|
||||||
|
//#if MC<11600
|
||||||
|
//$$ || packet instanceof EntitySpawnGlobalS2CPacket
|
||||||
|
//#endif
|
||||||
|
|| packet instanceof PaintingSpawnS2CPacket
|
||||||
|
|| packet instanceof ExperienceOrbSpawnS2CPacket
|
||||||
|
|| packet instanceof EntitiesDestroyS2CPacket;
|
||||||
|
if (!relevantPacket) {
|
||||||
|
return; // don't want to do it too often, only when there's likely to be a dead entity
|
||||||
|
}
|
||||||
|
|
||||||
|
mc.send(() -> {
|
||||||
|
ClientWorld world = mc.world;
|
||||||
|
if (world != null) {
|
||||||
|
removeDeadEntities(world);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeDeadEntities(ClientWorld world) {
|
||||||
|
//#if MC>=11700
|
||||||
|
//$$ // From the looks of it, this has now been resolved (thanks to EntityChangeListener)
|
||||||
|
//#elseif MC>=11400
|
||||||
|
// Note: Not sure if it's still required but there's this really handy method anyway
|
||||||
|
world.finishRemovingEntities();
|
||||||
|
//#else
|
||||||
|
//$$ Iterator<Entity> iter = world.loadedEntityList.iterator();
|
||||||
|
//$$ while (iter.hasNext()) {
|
||||||
|
//$$ Entity entity = iter.next();
|
||||||
|
//$$ if (entity.isDead) {
|
||||||
|
//$$ int chunkX = entity.chunkCoordX;
|
||||||
|
//$$ int chunkY = entity.chunkCoordZ;
|
||||||
|
//$$
|
||||||
|
//#if MC>=11400
|
||||||
|
//$$ if (entity.addedToChunk && world.getChunkProvider().provideChunk(chunkX, chunkY, false, false) != null) {
|
||||||
|
//#else
|
||||||
|
//#if MC>=10904
|
||||||
|
//$$ if (entity.addedToChunk && world.getChunkProvider().getLoadedChunk(chunkX, chunkY) != null) {
|
||||||
|
//#else
|
||||||
|
//$$ if (entity.addedToChunk && world.getChunkProvider().chunkExists(chunkX, chunkY)) {
|
||||||
|
//#endif
|
||||||
|
//#endif
|
||||||
|
//$$ world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity);
|
||||||
|
//$$ }
|
||||||
|
//$$
|
||||||
|
//$$ iter.remove();
|
||||||
|
//$$ world.onEntityRemoved(entity);
|
||||||
|
//$$ }
|
||||||
|
//$$
|
||||||
|
//$$ }
|
||||||
|
//#endif
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a packet and return the result.
|
* Process a packet and return the result.
|
||||||
* @param p The packet to process
|
* @param p The packet to process
|
||||||
@@ -627,7 +648,11 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
//#if MC>=11600
|
//#if MC>=11600
|
||||||
//#if MC>=11603
|
//#if MC>=11603
|
||||||
packet.getDimensionIds(),
|
packet.getDimensionIds(),
|
||||||
|
//#if MC>=11800
|
||||||
|
//$$ packet.registryManager(),
|
||||||
|
//#else
|
||||||
(net.minecraft.util.registry.DynamicRegistryManager.Impl) packet.getRegistryManager(),
|
(net.minecraft.util.registry.DynamicRegistryManager.Impl) packet.getRegistryManager(),
|
||||||
|
//#endif
|
||||||
packet.getDimensionType(),
|
packet.getDimensionType(),
|
||||||
//#else
|
//#else
|
||||||
//$$ packet.method_29443(),
|
//$$ packet.method_29443(),
|
||||||
@@ -824,7 +849,13 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return asyncMode ? processPacketAsync(p) : processPacketSync(p);
|
if (asyncMode) {
|
||||||
|
return processPacketAsync(p);
|
||||||
|
} else {
|
||||||
|
Packet fp = p;
|
||||||
|
mc.send(() -> processPacketSync(fp));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1085,6 +1116,10 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
// Synchronous packet processing //
|
// Synchronous packet processing //
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Even in sync mode, we send from another thread because mods may rely on that
|
||||||
|
private final ExecutorService syncSender = Executors.newSingleThreadExecutor(runnable ->
|
||||||
|
new Thread(runnable, "replaymod-sync-sender"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends all packets until the specified timestamp is reached (inclusive).
|
* Sends all packets until the specified timestamp is reached (inclusive).
|
||||||
* If the timestamp is smaller than the last packet sent, the replay is restarted from the beginning.
|
* If the timestamp is smaller than the last packet sent, the replay is restarted from the beginning.
|
||||||
@@ -1093,6 +1128,36 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
@Override
|
@Override
|
||||||
public void sendPacketsTill(int timestamp) {
|
public void sendPacketsTill(int timestamp) {
|
||||||
Preconditions.checkState(!asyncMode, "This method cannot be used in async mode. Use jumpToTime(int) instead.");
|
Preconditions.checkState(!asyncMode, "This method cannot be used in async mode. Use jumpToTime(int) instead.");
|
||||||
|
|
||||||
|
// Submit our target to the sender thread and track its progress
|
||||||
|
AtomicBoolean doneSending = new AtomicBoolean();
|
||||||
|
syncSender.submit(() -> {
|
||||||
|
try {
|
||||||
|
doSendPacketsTill(timestamp);
|
||||||
|
} finally {
|
||||||
|
doneSending.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Drain the task queue while we are sending (in case a mod blocks the io thread waiting for the main thread)
|
||||||
|
while (!doneSending.get()) {
|
||||||
|
executeTaskQueue();
|
||||||
|
|
||||||
|
// Wait until the sender thread has made progress
|
||||||
|
try {
|
||||||
|
//noinspection BusyWait
|
||||||
|
Thread.sleep(0, 100_000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything has been sent, drain the queue one last time
|
||||||
|
executeTaskQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSendPacketsTill(int timestamp) {
|
||||||
try {
|
try {
|
||||||
while (ctx == null && !terminate) { // Make sure channel is ready
|
while (ctx == null && !terminate) { // Make sure channel is ready
|
||||||
Thread.sleep(10);
|
Thread.sleep(10);
|
||||||
@@ -1112,7 +1177,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
loginPhase = true;
|
loginPhase = true;
|
||||||
startFromBeginning = false;
|
startFromBeginning = false;
|
||||||
nextPacket = null;
|
nextPacket = null;
|
||||||
replayHandler.restartedReplay();
|
ReplayMod.instance.runSync(replayHandler::restartedReplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replayIn == null) {
|
if (replayIn == null) {
|
||||||
@@ -1159,7 +1224,30 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Packet processPacketSync(Packet p) {
|
private void executeTaskQueue() {
|
||||||
|
//#if MC>=11400
|
||||||
|
((MCVer.MinecraftMethodAccessor) mc).replayModExecuteTaskQueue();
|
||||||
|
//#else
|
||||||
|
//$$ java.util.Queue<java.util.concurrent.FutureTask<?>> scheduledTasks = ((MinecraftAccessor) mc).getScheduledTasks();
|
||||||
|
//$$
|
||||||
|
//$$ // Live-lock detection: if we already hold the lock, then the sender thread will never be able to queue its
|
||||||
|
//$$ // tasks
|
||||||
|
//$$ if (Thread.holdsLock(scheduledTasks)) {
|
||||||
|
//$$ throw new IllegalStateException("Task queue already locked. " +
|
||||||
|
//$$ "You may want to use `Scheduler.runLaterWithoutLock` to run while the lock is not taken.");
|
||||||
|
//$$ }
|
||||||
|
//$$
|
||||||
|
//$$ //noinspection SynchronizationOnLocalVariableOrMethodParameter
|
||||||
|
//$$ synchronized (scheduledTasks) {
|
||||||
|
//$$ while (!scheduledTasks.isEmpty()) {
|
||||||
|
//$$ scheduledTasks.poll().run();
|
||||||
|
//$$ }
|
||||||
|
//$$ }
|
||||||
|
//#endif
|
||||||
|
ReplayMod.instance.runTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void processPacketSync(Packet p) {
|
||||||
//#if MC>=10904
|
//#if MC>=10904
|
||||||
if (p instanceof UnloadChunkS2CPacket) {
|
if (p instanceof UnloadChunkS2CPacket) {
|
||||||
UnloadChunkS2CPacket packet = (UnloadChunkS2CPacket) p;
|
UnloadChunkS2CPacket packet = (UnloadChunkS2CPacket) p;
|
||||||
@@ -1280,7 +1368,6 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p; // During synchronous playback everything is sent normally
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void forcePositionForVehicleAndSelf(Entity entity) {
|
private void forcePositionForVehicleAndSelf(Entity entity) {
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import com.replaymod.replay.camera.CameraEntity;
|
|||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.render.RenderTickCounter;
|
import net.minecraft.client.render.RenderTickCounter;
|
||||||
|
|
||||||
|
//#if MC>=11802
|
||||||
|
//$$ import net.minecraft.client.gui.screen.DownloadingTerrainScreen;
|
||||||
|
//#endif
|
||||||
|
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
//#else
|
//#else
|
||||||
@@ -111,6 +115,15 @@ public class InputReplayTimer extends WrappedTimer {
|
|||||||
//#endif
|
//#endif
|
||||||
//$$ }
|
//$$ }
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
|
//#if MC>=11802
|
||||||
|
//$$ // As of 1.18.2, this screen always stays open for at least two seconds, and requires ticking to close.
|
||||||
|
//$$ // Thanks, but we'll have none of that (at least while in a replay).
|
||||||
|
//$$ if (mc.currentScreen instanceof DownloadingTerrainScreen) {
|
||||||
|
//$$ mc.currentScreen.close();
|
||||||
|
//$$ }
|
||||||
|
//#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
//#if MC>=11600
|
//#if MC>=11600
|
||||||
return ticksThisFrame;
|
return ticksThisFrame;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//#if MC>=10904
|
//#if MC>=10800
|
||||||
package com.replaymod.replay;
|
package com.replaymod.replay;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
@@ -78,7 +78,11 @@ public class QuickReplaySender extends ChannelHandlerAdapter implements ReplaySe
|
|||||||
wrappedBuf.writerIndex(size);
|
wrappedBuf.writerIndex(size);
|
||||||
PacketByteBuf packetByteBuf = new PacketByteBuf(wrappedBuf);
|
PacketByteBuf packetByteBuf = new PacketByteBuf(wrappedBuf);
|
||||||
|
|
||||||
|
//#if MC>=10809
|
||||||
Packet<?> mcPacket;
|
Packet<?> mcPacket;
|
||||||
|
//#else
|
||||||
|
//$$ Packet mcPacket;
|
||||||
|
//#endif
|
||||||
//#if MC>=11700
|
//#if MC>=11700
|
||||||
//$$ mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId(), packetByteBuf);
|
//$$ mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId(), packetByteBuf);
|
||||||
//#elseif MC>=11500
|
//#elseif MC>=11500
|
||||||
@@ -137,13 +141,13 @@ public class QuickReplaySender extends ChannelHandlerAdapter implements ReplaySe
|
|||||||
LOGGER.info("Initialized quick replay sender in " + (System.currentTimeMillis() - start) + "ms");
|
LOGGER.info("Initialized quick replay sender in " + (System.currentTimeMillis() - start) + "ms");
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
LOGGER.error("Initializing quick replay sender:", e);
|
LOGGER.error("Initializing quick replay sender:", e);
|
||||||
mod.getCore().runLater(() -> {
|
mod.getCore().runLaterWithoutLock(() -> {
|
||||||
mod.getCore().printWarningToChat("Error initializing quick replay sender: %s", e.getLocalizedMessage());
|
mod.getCore().printWarningToChat("Error initializing quick replay sender: %s", e.getLocalizedMessage());
|
||||||
promise.setException(e);
|
promise.setException(e);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mod.getCore().runLater(() -> promise.set(null));
|
mod.getCore().runLaterWithoutLock(() -> promise.set(null));
|
||||||
}).start();
|
}).start();
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ import net.minecraft.entity.LivingEntity;
|
|||||||
//$$ import io.netty.channel.ChannelOutboundHandlerAdapter;
|
//$$ import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
//#if MC<10904
|
//#if MC<10800
|
||||||
//$$ import de.johni0702.minecraft.gui.element.GuiLabel;
|
//$$ import de.johni0702.minecraft.gui.element.GuiLabel;
|
||||||
//$$ import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
|
//$$ import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
|
||||||
//$$ import de.johni0702.minecraft.gui.utils.Colors;
|
//$$ import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
@@ -119,11 +119,11 @@ public class ReplayHandler {
|
|||||||
* Decodes and sends packets into channel.
|
* Decodes and sends packets into channel.
|
||||||
*/
|
*/
|
||||||
private final FullReplaySender fullReplaySender;
|
private final FullReplaySender fullReplaySender;
|
||||||
//#if MC>=10904
|
//#if MC>=10800
|
||||||
private final QuickReplaySender quickReplaySender;
|
private final QuickReplaySender quickReplaySender;
|
||||||
private boolean quickMode = false;
|
private boolean quickMode = false;
|
||||||
//#else
|
//#else
|
||||||
//$$ private static final String QUICK_MODE_MIN_MC = "1.9.4";
|
//$$ private static final String QUICK_MODE_MIN_MC = "1.8";
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,7 +160,7 @@ public class ReplayHandler {
|
|||||||
markers = replayFile.getMarkers().or(Collections.emptySet());
|
markers = replayFile.getMarkers().or(Collections.emptySet());
|
||||||
|
|
||||||
fullReplaySender = new FullReplaySender(this, replayFile, false);
|
fullReplaySender = new FullReplaySender(this, replayFile, false);
|
||||||
//#if MC>=10904
|
//#if MC>=10800
|
||||||
quickReplaySender = new QuickReplaySender(ReplayModReplay.instance, replayFile);
|
quickReplaySender = new QuickReplaySender(ReplayModReplay.instance, replayFile);
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ public class ReplayHandler {
|
|||||||
ReplayClosingCallback.EVENT.invoker().replayClosing(this);
|
ReplayClosingCallback.EVENT.invoker().replayClosing(this);
|
||||||
|
|
||||||
fullReplaySender.terminateReplay();
|
fullReplaySender.terminateReplay();
|
||||||
//#if MC>=10904
|
//#if MC>=10800
|
||||||
if (quickMode) {
|
if (quickMode) {
|
||||||
quickReplaySender.unregister();
|
quickReplaySender.unregister();
|
||||||
}
|
}
|
||||||
@@ -308,7 +308,7 @@ public class ReplayHandler {
|
|||||||
//$$ channel = new EmbeddedChannel(dummyHandler);
|
//$$ channel = new EmbeddedChannel(dummyHandler);
|
||||||
//$$ channel.pipeline().remove(dummyHandler);
|
//$$ channel.pipeline().remove(dummyHandler);
|
||||||
//#endif
|
//#endif
|
||||||
//#if MC>=10904
|
//#if MC>=10800
|
||||||
channel.pipeline().addLast("ReplayModReplay_quickReplaySender", quickReplaySender);
|
channel.pipeline().addLast("ReplayModReplay_quickReplaySender", quickReplaySender);
|
||||||
//#endif
|
//#endif
|
||||||
channel.pipeline().addLast("ReplayModReplay_replaySender", fullReplaySender);
|
channel.pipeline().addLast("ReplayModReplay_replaySender", fullReplaySender);
|
||||||
@@ -329,7 +329,7 @@ public class ReplayHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ReplaySender getReplaySender() {
|
public ReplaySender getReplaySender() {
|
||||||
//#if MC>=10904
|
//#if MC>=10800
|
||||||
return quickMode ? quickReplaySender : fullReplaySender;
|
return quickMode ? quickReplaySender : fullReplaySender;
|
||||||
//#else
|
//#else
|
||||||
//$$ return fullReplaySender;
|
//$$ return fullReplaySender;
|
||||||
@@ -340,7 +340,7 @@ public class ReplayHandler {
|
|||||||
return overlay;
|
return overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#if MC>=10904
|
//#if MC>=10800
|
||||||
public void ensureQuickModeInitialized(Runnable andThen) {
|
public void ensureQuickModeInitialized(Runnable andThen) {
|
||||||
if (Utils.ifMinimalModeDoPopup(overlay, () -> {})) return;
|
if (Utils.ifMinimalModeDoPopup(overlay, () -> {})) return;
|
||||||
ListenableFuture<Void> future = quickReplaySender.getInitializationPromise();
|
ListenableFuture<Void> future = quickReplaySender.getInitializationPromise();
|
||||||
@@ -534,7 +534,11 @@ public class ReplayHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void doJump(int targetTime, boolean retainCameraPosition) {
|
public void doJump(int targetTime, boolean retainCameraPosition) {
|
||||||
//#if MC>=10904
|
if (!getReplaySender().isAsyncMode()) {
|
||||||
|
return; // path playback, rendering, etc. -> no jumping allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if MC>=10800
|
||||||
if (getReplaySender() == quickReplaySender) {
|
if (getReplaySender() == quickReplaySender) {
|
||||||
// Always round to full tick
|
// Always round to full tick
|
||||||
targetTime = targetTime + targetTime % 50;
|
targetTime = targetTime + targetTime % 50;
|
||||||
@@ -603,6 +607,7 @@ public class ReplayHandler {
|
|||||||
// Render our please-wait-screen
|
// Render our please-wait-screen
|
||||||
GuiScreen guiScreen = new GuiScreen();
|
GuiScreen guiScreen = new GuiScreen();
|
||||||
guiScreen.setBackground(AbstractGuiScreen.Background.DIRT);
|
guiScreen.setBackground(AbstractGuiScreen.Background.DIRT);
|
||||||
|
guiScreen.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER));
|
||||||
guiScreen.addElements(new HorizontalLayout.Data(0.5),
|
guiScreen.addElements(new HorizontalLayout.Data(0.5),
|
||||||
new GuiLabel().setI18nText("replaymod.gui.pleasewait"));
|
new GuiLabel().setI18nText("replaymod.gui.pleasewait"));
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ import net.minecraft.stat.StatHandler;
|
|||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.Box;
|
import net.minecraft.util.math.Box;
|
||||||
|
|
||||||
|
//#if MC>=11802
|
||||||
|
//$$ import net.minecraft.tag.TagKey;
|
||||||
|
//#endif
|
||||||
|
|
||||||
//#if FABRIC>=1
|
//#if FABRIC>=1
|
||||||
//#else
|
//#else
|
||||||
//$$ import net.minecraftforge.client.event.EntityViewRenderEvent;
|
//$$ import net.minecraftforge.client.event.EntityViewRenderEvent;
|
||||||
@@ -354,7 +358,13 @@ public class CameraEntity
|
|||||||
|
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
@Override
|
@Override
|
||||||
public boolean isSubmergedIn(Tag<Fluid> fluid) {
|
public boolean isSubmergedIn(
|
||||||
|
//#if MC>=11802
|
||||||
|
//$$ TagKey<Fluid> fluid
|
||||||
|
//#else
|
||||||
|
Tag<Fluid> fluid
|
||||||
|
//#endif
|
||||||
|
) {
|
||||||
return falseUnlessSpectating(entity -> entity.isSubmergedIn(fluid));
|
return falseUnlessSpectating(entity -> entity.isSubmergedIn(fluid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ import java.io.File;
|
|||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -95,8 +97,10 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
|
|
||||||
List<GuiReplayEntry> selected = list.getSelected();
|
List<GuiReplayEntry> selected = list.getSelected();
|
||||||
if (selected.size() == 1) {
|
if (selected.size() == 1) {
|
||||||
|
File file = selected.get(0).file;
|
||||||
|
LOGGER.info("Opening replay in viewer: " + file);
|
||||||
try {
|
try {
|
||||||
mod.startReplay(selected.get(0).file);
|
mod.startReplay(file);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -130,8 +134,8 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
public final GuiButton renameButton = new GuiButton().onClick(new Runnable() {
|
public final GuiButton renameButton = new GuiButton().onClick(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final File file = list.getSelected().get(0).file;
|
final Path path = list.getSelected().get(0).file.toPath();
|
||||||
String name = Utils.fileNameToReplayName(file.getName());
|
String name = Utils.fileNameToReplayName(path.getFileName().toString());
|
||||||
final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(name);
|
final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(name);
|
||||||
final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this,
|
final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this,
|
||||||
new GuiLabel().setI18nText("replaymod.gui.viewer.rename.name").setColor(Colors.BLACK),
|
new GuiLabel().setI18nText("replaymod.gui.viewer.rename.name").setColor(Colors.BLACK),
|
||||||
@@ -147,16 +151,16 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
}
|
}
|
||||||
}).onTextChanged(obj -> {
|
}).onTextChanged(obj -> {
|
||||||
popup.getYesButton().setEnabled(!nameField.getText().isEmpty()
|
popup.getYesButton().setEnabled(!nameField.getText().isEmpty()
|
||||||
&& !new File(file.getParentFile(), Utils.replayNameToFileName(nameField.getText())).exists());
|
&& Files.notExists(Utils.replayNameToPath(path.getParent(), nameField.getText())));
|
||||||
});
|
});
|
||||||
popup.onAccept(() -> {
|
popup.onAccept(() -> {
|
||||||
// Sanitize their input
|
// Sanitize their input
|
||||||
String newName = nameField.getText().trim();
|
String newName = nameField.getText().trim();
|
||||||
// This file is what they want
|
// This file is what they want
|
||||||
File targetFile = new File(file.getParentFile(), Utils.replayNameToFileName(newName));
|
Path targetPath = Utils.replayNameToPath(path.getParent(), newName);
|
||||||
try {
|
try {
|
||||||
// Finally, try to move it
|
// Finally, try to move it
|
||||||
FileUtils.moveFile(file, targetFile);
|
Files.move(path, targetPath);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// We failed (might also be their OS)
|
// We failed (might also be their OS)
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public abstract class MixinGuiSpectator {
|
|||||||
//$$ @Inject(method = "func_175260_a", at = @At("HEAD"), cancellable = true)
|
//$$ @Inject(method = "func_175260_a", at = @At("HEAD"), cancellable = true)
|
||||||
//#endif
|
//#endif
|
||||||
public void isInReplay(
|
public void isInReplay(
|
||||||
//#if MC>=11400
|
//#if MC>=11400 && MC<11802
|
||||||
double i,
|
double i,
|
||||||
//#else
|
//#else
|
||||||
//$$ int i,
|
//$$ int i,
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
//#if MC>=11400
|
||||||
|
package com.replaymod.replay.mixin;
|
||||||
|
|
||||||
|
import com.replaymod.replay.camera.CameraEntity;
|
||||||
|
import net.minecraft.client.network.ClientPlayerEntity;
|
||||||
|
import net.minecraft.client.network.ClientPlayerInteractionManager;
|
||||||
|
import net.minecraft.world.GameMode;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Pseudo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import static com.replaymod.core.versions.MCVer.getMinecraft;
|
||||||
|
|
||||||
|
@Pseudo
|
||||||
|
@Mixin(targets = "net.coderbot.iris.pipeline.HandRenderer", remap = false)
|
||||||
|
public abstract class Mixin_ShowSpectatedHand_Iris {
|
||||||
|
@Redirect(
|
||||||
|
method = "*",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;getCurrentGameMode()Lnet/minecraft/world/GameMode;",
|
||||||
|
remap = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private GameMode getGameMode(ClientPlayerInteractionManager interactionManager) {
|
||||||
|
ClientPlayerEntity camera = getMinecraft().player;
|
||||||
|
if (camera instanceof CameraEntity) {
|
||||||
|
// alternative doesn't really matter, the caller only checks for equality to SPECTATOR
|
||||||
|
return camera.isSpectator() ? GameMode.SPECTATOR : GameMode.SURVIVAL;
|
||||||
|
}
|
||||||
|
return interactionManager.getCurrentGameMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endif
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.replaymod.replay.mixin.world_border;
|
||||||
|
|
||||||
|
import com.replaymod.core.versions.MCVer;
|
||||||
|
import com.replaymod.replay.ReplayHandler;
|
||||||
|
import com.replaymod.replay.ReplayModReplay;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normally Minecraft's world border movement is based off real time;
|
||||||
|
* this redirect ensures that it is synced with the time in the Replay instead.
|
||||||
|
*/
|
||||||
|
//#if MC>=11400
|
||||||
|
// FIXME: preprocessor should be able to remap between fabric and forge
|
||||||
|
//#if FABRIC
|
||||||
|
@Mixin(targets = "net.minecraft.world.border.WorldBorder.MovingArea")
|
||||||
|
//#else
|
||||||
|
//$$ @Mixin(targets = "net.minecraft.world.border.WorldBorder.MovingBorderInfo")
|
||||||
|
//#endif
|
||||||
|
//#else
|
||||||
|
//$$ @Mixin(net.minecraft.world.border.WorldBorder.class)
|
||||||
|
//#endif
|
||||||
|
public class Mixin_UseReplayTime_ForMovement {
|
||||||
|
|
||||||
|
//#if MC>=11400
|
||||||
|
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Util;getMeasuringTimeMs()J"))
|
||||||
|
//#else
|
||||||
|
//$$ @Redirect(method = "*", at = @At(value = "INVOKE", target = "Ljava/lang/System;currentTimeMillis()J"))
|
||||||
|
//#endif
|
||||||
|
private long getWorldBorderTime() {
|
||||||
|
ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler();
|
||||||
|
if (replayHandler != null) {
|
||||||
|
return replayHandler.getReplaySender().currentTimeStamp();
|
||||||
|
}
|
||||||
|
return MCVer.milliTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.replaymod.replay.mixin.world_border;
|
||||||
|
|
||||||
|
import com.replaymod.core.versions.MCVer;
|
||||||
|
import com.replaymod.replay.ReplayHandler;
|
||||||
|
import com.replaymod.replay.ReplayModReplay;
|
||||||
|
import net.minecraft.client.render.WorldRenderer;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normally Minecraft's world border texture animation is based off real time;
|
||||||
|
* this redirect ensures that it is synced with the time in the Replay instead.
|
||||||
|
*/
|
||||||
|
@Mixin(WorldRenderer.class)
|
||||||
|
public class Mixin_UseReplayTime_ForTexture {
|
||||||
|
//#if MC>=11400
|
||||||
|
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Util;getMeasuringTimeMs()J"))
|
||||||
|
//#else
|
||||||
|
//$$ @Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getSystemTime()J"))
|
||||||
|
//#endif
|
||||||
|
private long getWorldBorderTime() {
|
||||||
|
ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler();
|
||||||
|
if (replayHandler != null) {
|
||||||
|
return replayHandler.getReplaySender().currentTimeStamp();
|
||||||
|
}
|
||||||
|
return MCVer.milliTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,10 +9,49 @@ uniform int direction;
|
|||||||
|
|
||||||
const float eyeDistance = 0.14;
|
const float eyeDistance = 0.14;
|
||||||
|
|
||||||
|
void orient(vec4 position, int orientation) {
|
||||||
|
float z;
|
||||||
|
if (orientation == 0) { // LEFT
|
||||||
|
z = position.z;
|
||||||
|
position.z = position.x;
|
||||||
|
position.x = -z;
|
||||||
|
} else if (orientation == 1) { // RIGHT
|
||||||
|
z = position.z;
|
||||||
|
position.z = -position.x;
|
||||||
|
position.x = z;
|
||||||
|
} else if (orientation == 2) { // FRONT
|
||||||
|
// No changes required
|
||||||
|
} else if (orientation == 3) { // BACK
|
||||||
|
position.x = -position.x;
|
||||||
|
position.z = -position.z;
|
||||||
|
} else if (orientation == 4) { // TOP
|
||||||
|
z = position.z;
|
||||||
|
position.z = -position.y;
|
||||||
|
position.y = z;
|
||||||
|
} else if (orientation == 5) { // BOTTOM
|
||||||
|
z = position.z;
|
||||||
|
position.z = position.y;
|
||||||
|
position.y = -z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void orientInverse(vec4 position, int orientation) {
|
||||||
|
if (orientation < 2) {
|
||||||
|
orient(position, 1 - orientation); // LEFT and RIGHT flip
|
||||||
|
} else if (orientation < 4) {
|
||||||
|
orient(position, orientation); // FRONT and BACK are their own inverses
|
||||||
|
} else {
|
||||||
|
orient(position, (1 - (orientation - 4)) + 4); // TOP and BOTTOM flip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// Transform to view space
|
// Transform to view space
|
||||||
vec4 position = gl_ModelViewMatrix * gl_Vertex;
|
vec4 position = gl_ModelViewMatrix * gl_Vertex;
|
||||||
|
|
||||||
|
// Undo the camera rotation, so we always apply our stereo effect looking in the same direction
|
||||||
|
orientInverse(position, direction);
|
||||||
|
|
||||||
// Distort for ODS
|
// Distort for ODS
|
||||||
// O := The origin
|
// O := The origin
|
||||||
// P := The current vertex/point
|
// P := The current vertex/point
|
||||||
@@ -34,30 +73,8 @@ void main() {
|
|||||||
// Calculate the vector between O and T and finally move the vertex by that vector
|
// Calculate the vector between O and T and finally move the vertex by that vector
|
||||||
position -= vec4(distTO * sin(angOT), 0, distTO * cos(angOT), 0);
|
position -= vec4(distTO * sin(angOT), 0, distTO * cos(angOT), 0);
|
||||||
|
|
||||||
// Rotate for different cubic views
|
// Rotate back into the correct cubic view
|
||||||
float z;
|
orient(position, direction);
|
||||||
if (direction == 0) { // LEFT
|
|
||||||
z = position.z;
|
|
||||||
position.z = position.x;
|
|
||||||
position.x = -z;
|
|
||||||
} else if (direction == 1) { // RIGHT
|
|
||||||
z = position.z;
|
|
||||||
position.z = -position.x;
|
|
||||||
position.x = z;
|
|
||||||
} else if (direction == 2) { // FRONT
|
|
||||||
// No changes required
|
|
||||||
} else if (direction == 3) { // BACK
|
|
||||||
position.x = -position.x;
|
|
||||||
position.z = -position.z;
|
|
||||||
} else if (direction == 4) { // TOP
|
|
||||||
z = position.z;
|
|
||||||
position.z = -position.y;
|
|
||||||
position.y = z;
|
|
||||||
} else if (direction == 5) { // BOTTOM
|
|
||||||
z = position.z;
|
|
||||||
position.z = position.y;
|
|
||||||
position.y = -z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform to screen space
|
// Transform to screen space
|
||||||
gl_Position = gl_ProjectionMatrix * position;
|
gl_Position = gl_ProjectionMatrix * position;
|
||||||
|
|||||||
Submodule src/main/resources/assets/replaymod/lang updated: 54ab62bdbb...7614b69e24
@@ -14,10 +14,49 @@ uniform int direction;
|
|||||||
|
|
||||||
const float eyeDistance = 0.14;
|
const float eyeDistance = 0.14;
|
||||||
|
|
||||||
|
void orient(vec4 position, int orientation) {
|
||||||
|
float z;
|
||||||
|
if (orientation == 0) { // LEFT
|
||||||
|
z = position.z;
|
||||||
|
position.z = position.x;
|
||||||
|
position.x = -z;
|
||||||
|
} else if (orientation == 1) { // RIGHT
|
||||||
|
z = position.z;
|
||||||
|
position.z = -position.x;
|
||||||
|
position.x = z;
|
||||||
|
} else if (orientation == 2) { // FRONT
|
||||||
|
// No changes required
|
||||||
|
} else if (orientation == 3) { // BACK
|
||||||
|
position.x = -position.x;
|
||||||
|
position.z = -position.z;
|
||||||
|
} else if (orientation == 4) { // TOP
|
||||||
|
z = position.z;
|
||||||
|
position.z = -position.y;
|
||||||
|
position.y = z;
|
||||||
|
} else if (orientation == 5) { // BOTTOM
|
||||||
|
z = position.z;
|
||||||
|
position.z = position.y;
|
||||||
|
position.y = -z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void orientInverse(vec4 position, int orientation) {
|
||||||
|
if (orientation < 2) {
|
||||||
|
orient(position, 1 - orientation); // LEFT and RIGHT flip
|
||||||
|
} else if (orientation < 4) {
|
||||||
|
orient(position, orientation); // FRONT and BACK are their own inverses
|
||||||
|
} else {
|
||||||
|
orient(position, (1 - (orientation - 4)) + 4); // TOP and BOTTOM flip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// Transform to view space
|
// Transform to view space
|
||||||
vec4 position = gl_ModelViewMatrix * gl_Vertex;
|
vec4 position = gl_ModelViewMatrix * gl_Vertex;
|
||||||
|
|
||||||
|
// Undo the camera rotation, so we always apply our stereo effect looking in the same direction
|
||||||
|
orientInverse(position, direction);
|
||||||
|
|
||||||
// Distort for ODS
|
// Distort for ODS
|
||||||
// O := The origin
|
// O := The origin
|
||||||
// P := The current vertex/point
|
// P := The current vertex/point
|
||||||
@@ -39,30 +78,8 @@ void main() {
|
|||||||
// Calculate the vector between O and T and finally move the vertex by that vector
|
// Calculate the vector between O and T and finally move the vertex by that vector
|
||||||
position -= vec4(distTO * sin(angOT), 0, distTO * cos(angOT), 0);
|
position -= vec4(distTO * sin(angOT), 0, distTO * cos(angOT), 0);
|
||||||
|
|
||||||
// Rotate for different cubic views
|
// Rotate back into the correct cubic view
|
||||||
float z;
|
orient(position, direction);
|
||||||
if (direction == 0) { // LEFT
|
|
||||||
z = position.z;
|
|
||||||
position.z = position.x;
|
|
||||||
position.x = -z;
|
|
||||||
} else if (direction == 1) { // RIGHT
|
|
||||||
z = position.z;
|
|
||||||
position.z = -position.x;
|
|
||||||
position.x = z;
|
|
||||||
} else if (direction == 2) { // FRONT
|
|
||||||
// No changes required
|
|
||||||
} else if (direction == 3) { // BACK
|
|
||||||
position.x = -position.x;
|
|
||||||
position.z = -position.z;
|
|
||||||
} else if (direction == 4) { // TOP
|
|
||||||
z = position.z;
|
|
||||||
position.z = -position.y;
|
|
||||||
position.y = z;
|
|
||||||
} else if (direction == 5) { // BOTTOM
|
|
||||||
z = position.z;
|
|
||||||
position.z = position.y;
|
|
||||||
position.y = -z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform to screen space
|
// Transform to screen space
|
||||||
gl_Position = gl_ProjectionMatrix * position;
|
gl_Position = gl_ProjectionMatrix * position;
|
||||||
|
|||||||
@@ -47,6 +47,10 @@
|
|||||||
"fabric-resource-loader-v0": "*"
|
"fabric-resource-loader-v0": "*"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"conflicts": {
|
||||||
|
"iris": "<1.1.3"
|
||||||
|
},
|
||||||
|
|
||||||
"custom": {
|
"custom": {
|
||||||
"mm:early_risers": [
|
"mm:early_risers": [
|
||||||
"com.replaymod.core.ReplayModMMLauncher"
|
"com.replaymod.core.ReplayModMMLauncher"
|
||||||
|
|||||||
@@ -11,8 +11,10 @@
|
|||||||
"SPacketSpawnMobAccessor",
|
"SPacketSpawnMobAccessor",
|
||||||
"SPacketSpawnPlayerAccessor",
|
"SPacketSpawnPlayerAccessor",
|
||||||
"MixinServerInfo",
|
"MixinServerInfo",
|
||||||
//#if MC>=11400
|
//#if MC>=10800
|
||||||
"MixinDownloadingPackFinder",
|
"MixinDownloadingPackFinder",
|
||||||
|
//#endif
|
||||||
|
//#if MC>=11400
|
||||||
"MixinMouseHelper",
|
"MixinMouseHelper",
|
||||||
//#endif
|
//#endif
|
||||||
//#if MC>=10904
|
//#if MC>=10904
|
||||||
|
|||||||
@@ -14,9 +14,7 @@
|
|||||||
"Mixin_HideNameTags",
|
"Mixin_HideNameTags",
|
||||||
"Mixin_HideNameTags_LivingEntity",
|
"Mixin_HideNameTags_LivingEntity",
|
||||||
"Mixin_Omnidirectional_Camera",
|
"Mixin_Omnidirectional_Camera",
|
||||||
"Mixin_Omnidirectional_DisableFrustumCulling",
|
|
||||||
"Mixin_Omnidirectional_Rotation",
|
"Mixin_Omnidirectional_Rotation",
|
||||||
"Mixin_Omnidirectional_SkipHand",
|
|
||||||
"Mixin_PreserveDepthDuringGuiRendering",
|
"Mixin_PreserveDepthDuringGuiRendering",
|
||||||
"Mixin_SkipBlockOutlinesDuringRender",
|
"Mixin_SkipBlockOutlinesDuringRender",
|
||||||
"Mixin_SkipHudDuringRender",
|
"Mixin_SkipHudDuringRender",
|
||||||
@@ -39,6 +37,7 @@
|
|||||||
"Mixin_PreserveDepthDuringHandRendering",
|
"Mixin_PreserveDepthDuringHandRendering",
|
||||||
"Mixin_WindowsWorkaroundForTinyEXRNatives",
|
"Mixin_WindowsWorkaroundForTinyEXRNatives",
|
||||||
//#endif
|
//#endif
|
||||||
|
"GameRendererAccessor",
|
||||||
"MainWindowAccessor",
|
"MainWindowAccessor",
|
||||||
"WorldRendererAccessor",
|
"WorldRendererAccessor",
|
||||||
//#if MC>=10904
|
//#if MC>=10904
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
"client": [
|
"client": [
|
||||||
"entity_tracking.Mixin_EntityExt",
|
"entity_tracking.Mixin_EntityExt",
|
||||||
"entity_tracking.Mixin_FixPartialUpdates",
|
"entity_tracking.Mixin_FixPartialUpdates",
|
||||||
|
"world_border.Mixin_UseReplayTime_ForMovement",
|
||||||
|
"world_border.Mixin_UseReplayTime_ForTexture",
|
||||||
"Mixin_FixNPCSkinCaching",
|
"Mixin_FixNPCSkinCaching",
|
||||||
//#if MC>=11800
|
//#if MC>=11800
|
||||||
//$$ "Mixin_FixEntityNotTracking",
|
//$$ "Mixin_FixEntityNotTracking",
|
||||||
@@ -23,6 +25,7 @@
|
|||||||
"ClientWorldAccessor",
|
"ClientWorldAccessor",
|
||||||
"EntityLivingBaseAccessor",
|
"EntityLivingBaseAccessor",
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
|
"Mixin_ShowSpectatedHand_Iris",
|
||||||
"Mixin_ShowSpectatedHand_NoOF",
|
"Mixin_ShowSpectatedHand_NoOF",
|
||||||
"Mixin_ShowSpectatedHand_OF",
|
"Mixin_ShowSpectatedHand_OF",
|
||||||
//#else
|
//#else
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
{
|
{
|
||||||
"pack": {
|
"pack": {
|
||||||
"description": "ReplayMod resources",
|
"description": "ReplayMod resources",
|
||||||
//#if MC>=11400
|
|
||||||
"pack_format": 4
|
"pack_format": 4
|
||||||
//#else
|
|
||||||
//#if MC>=11002
|
|
||||||
//$$ "pack_format": 2
|
|
||||||
//#else
|
|
||||||
//$$ "pack_format": 1
|
|
||||||
//#endif
|
|
||||||
//#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.6.3
|
2.6.4
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ public class Window implements MainWindowAccessor {
|
|||||||
mc.displayHeight = value;
|
mc.displayHeight = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invokeUpdateFramebufferSize() {
|
||||||
|
// no-op, pre-LWJGL3 MC doesn't differentiate between window and framebuffer size
|
||||||
|
}
|
||||||
|
|
||||||
public long getHandle() {
|
public long getHandle() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.replaymod.render.mixin;
|
|||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||||
|
|
||||||
@Mixin(Minecraft.class)
|
@Mixin(Minecraft.class)
|
||||||
public interface MainWindowAccessor {
|
public interface MainWindowAccessor {
|
||||||
@@ -14,4 +15,6 @@ public interface MainWindowAccessor {
|
|||||||
int getFramebufferHeight();
|
int getFramebufferHeight();
|
||||||
@Accessor("displayHeight")
|
@Accessor("displayHeight")
|
||||||
void setFramebufferHeight(int value);
|
void setFramebufferHeight(int value);
|
||||||
|
@Invoker
|
||||||
|
void invokeUpdateFramebufferSize();
|
||||||
}
|
}
|
||||||
|
|||||||
6
versions/1.12.2/src/main/resources/pack.mcmeta
Normal file
6
versions/1.12.2/src/main/resources/pack.mcmeta
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"pack": {
|
||||||
|
"description": "ReplayMod resources",
|
||||||
|
"pack_format": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
0
versions/1.18.2/.gitkeep
Normal file
0
versions/1.18.2/.gitkeep
Normal file
6
versions/1.9.4/src/main/resources/pack.mcmeta
Normal file
6
versions/1.9.4/src/main/resources/pack.mcmeta
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"pack": {
|
||||||
|
"description": "ReplayMod resources",
|
||||||
|
"pack_format": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
net.minecraft.client.network.ClientPlayerEntity getUnderwaterVisibility() method_3140()
|
net.minecraft.client.network.ClientPlayerEntity getUnderwaterVisibility() method_3140()
|
||||||
|
net.minecraft.client.render.GameRenderer getBasicProjectionMatrix() method_22973()
|
||||||
|
|||||||
Reference in New Issue
Block a user