Compare commits
39 Commits
1.9.4-2.0.
...
1.8-2.0.0-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
682fb4bfdc | ||
|
|
0c48d6a1dd | ||
|
|
0ba5b0aaad | ||
|
|
0db1b9ef5a | ||
|
|
e5eb982b98 | ||
|
|
3a28c5ba6f | ||
|
|
636ce7bdd4 | ||
|
|
92eb11a617 | ||
|
|
9ff767b054 | ||
|
|
d82cef3687 | ||
|
|
cc65b26c31 | ||
|
|
c8b71dc2cc | ||
|
|
b240f8a276 | ||
|
|
4805e5e9f5 | ||
|
|
18f83039af | ||
|
|
fcd4c0788e | ||
|
|
9c605baa7e | ||
|
|
721fdc4d86 | ||
|
|
1851e97346 | ||
|
|
d547098ce8 | ||
|
|
b2c7faac6f | ||
|
|
4e6b387f97 | ||
|
|
21bbeb2235 | ||
|
|
b5a6d8b307 | ||
|
|
dd0537b8fd | ||
|
|
e35bfcaa58 | ||
|
|
6d0cdfbcb4 | ||
|
|
a804d6b8a3 | ||
|
|
45191daad4 | ||
|
|
9de63ce9b1 | ||
|
|
2a7542aeb8 | ||
|
|
c93a3e384c | ||
|
|
8bb5d11cb4 | ||
|
|
f3142a714c | ||
|
|
3abaa1bcbb | ||
|
|
4b47f2cbf8 | ||
|
|
ada460c384 | ||
|
|
ad62060368 | ||
|
|
82246a5f48 |
Submodule ReplayStudio updated: f98b781f30...9591df75a1
83
build.gradle
83
build.gradle
@@ -1,3 +1,5 @@
|
||||
import groovy.json.JsonOutput
|
||||
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
buildscript {
|
||||
@@ -195,4 +197,85 @@ setupDecompWorkspace.dependsOn copySrg
|
||||
setupDevWorkspace.dependsOn copySrg
|
||||
project.tasks.idea.dependsOn copySrg
|
||||
|
||||
def generateVersionsJson() {
|
||||
// List all tags
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
project.exec {
|
||||
commandLine 'git', 'for-each-ref', '--sort=taggerdate', '--format=%(refname:short)', 'refs/tags'
|
||||
standardOutput = stdout
|
||||
}
|
||||
def versions = stdout.toString().tokenize('\n').reverse()
|
||||
def mcVersions = versions.collect {it.substring(0, it.indexOf('-'))}.unique().reverse()
|
||||
|
||||
def root = [
|
||||
homepage: 'https://www.replaymod.com/download/',
|
||||
promos: [:]
|
||||
]
|
||||
mcVersions.forEach { mcVersion ->
|
||||
def mcVersionRoot = [:]
|
||||
def latest
|
||||
def recommended
|
||||
versions.forEach {
|
||||
def (thisMcVersion, modVersion, preVersion) = it.tokenize('-')
|
||||
if (thisMcVersion == mcVersion) {
|
||||
mcVersionRoot[it] = "See https://github.com/ReplayMod/ReplayMod/releases/$it"
|
||||
if (latest == null) latest = it
|
||||
if (preVersion == null) {
|
||||
if (recommended == null) recommended = it
|
||||
}
|
||||
}
|
||||
}
|
||||
root[mcVersion] = mcVersionRoot
|
||||
root.promos[mcVersion + '-latest'] = latest
|
||||
if (recommended != null) {
|
||||
root.promos[mcVersion + '-recommended'] = recommended
|
||||
}
|
||||
}
|
||||
root
|
||||
}
|
||||
|
||||
task doRelease() {
|
||||
doLast {
|
||||
// Parse version
|
||||
def version = project.releaseVersion as String
|
||||
if (project.version.endsWith('*')) {
|
||||
throw new InvalidUserDataException('Git working tree is dirty. Make sure to commit all changes.')
|
||||
}
|
||||
def (mcVersion, modVersion, preVersion) = version.tokenize('-')
|
||||
preVersion = preVersion != null && preVersion.startsWith('b') ? preVersion.substring(1) : null
|
||||
|
||||
// Create new tag
|
||||
if (preVersion != null) {
|
||||
project.exec {
|
||||
commandLine 'git', 'tag',
|
||||
'-m', "Pre-release $preVersion of $modVersion for Minecraft $mcVersion",
|
||||
"$mcVersion-$modVersion-b$preVersion"
|
||||
}
|
||||
} else {
|
||||
project.exec {
|
||||
commandLine 'git', 'tag',
|
||||
'-m', "Release $modVersion for Minecraft $mcVersion",
|
||||
"$mcVersion-$modVersion"
|
||||
}
|
||||
}
|
||||
|
||||
// Switch to master branch to update versions.json
|
||||
project.exec { commandLine 'git', 'checkout', 'master' }
|
||||
|
||||
// Generate versions.json content
|
||||
def versionsRoot = generateVersionsJson()
|
||||
def versionsJson = JsonOutput.prettyPrint(JsonOutput.toJson(versionsRoot))
|
||||
|
||||
// Write versions.json
|
||||
new File('versions.json').write(versionsJson)
|
||||
|
||||
// Commit changes
|
||||
project.exec { commandLine 'git', 'add', 'versions.json' }
|
||||
project.exec { commandLine 'git', 'commit', '-m', "Update versions.json for $version" }
|
||||
|
||||
// Return to previous branch
|
||||
project.exec { commandLine 'git', 'checkout', '-' }
|
||||
}
|
||||
}
|
||||
|
||||
defaultTasks 'build'
|
||||
|
||||
2
jGui
2
jGui
Submodule jGui updated: aa68206b5e...163cfe2f8a
23
src/main/java/com/replaymod/compat/ReplayModCompat.java
Normal file
23
src/main/java/com/replaymod/compat/ReplayModCompat.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.replaymod.compat;
|
||||
|
||||
import com.replaymod.compat.shaders.ShaderBeginRender;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventBus;
|
||||
|
||||
@Mod(modid = ReplayModCompat.MOD_ID, useMetadata = true)
|
||||
public class ReplayModCompat {
|
||||
public static final String MOD_ID = "replaymod-compat";
|
||||
|
||||
@Mod.Instance(ReplayMod.MOD_ID)
|
||||
private static ReplayMod core;
|
||||
|
||||
@Mod.EventHandler
|
||||
public void init(FMLInitializationEvent event) {
|
||||
EventBus bus = FMLCommonHandler.instance().bus();
|
||||
bus.register(new ShaderBeginRender());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.replaymod.compat.shaders;
|
||||
|
||||
import com.replaymod.render.hooks.EntityRendererHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class ShaderBeginRender {
|
||||
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
/**
|
||||
* Invokes Shaders#beginRender when rendering a video,
|
||||
* as this would usually get called by EntityRenderer#renderWorld,
|
||||
* which we're not calling during rendering.
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public void onRenderTickStart(TickEvent.RenderTickEvent event) {
|
||||
if (event.phase != TickEvent.Phase.START) return;
|
||||
if (ShaderReflection.shaders_beginRender == null) return;
|
||||
if (ShaderReflection.config_isShaders == null) return;
|
||||
|
||||
try {
|
||||
// check if video is being rendered
|
||||
if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null)
|
||||
return;
|
||||
|
||||
// check if Shaders are enabled
|
||||
if (!(boolean) (ShaderReflection.config_isShaders.invoke(null))) return;
|
||||
|
||||
ShaderReflection.shaders_beginRender.invoke(null, mc, mc.timer.elapsedPartialTicks, 0);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.replaymod.compat.shaders;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class ShaderReflection {
|
||||
|
||||
// Shaders.frameTimeCounter
|
||||
public static Field shaders_frameTimeCounter;
|
||||
|
||||
// Shaders.isShadowPass
|
||||
public static Field shaders_isShadowPass;
|
||||
|
||||
// Shaders.beginRender()
|
||||
public static Method shaders_beginRender;
|
||||
|
||||
// RenderGlobal.chunksToUpdateForced (Optifine only)
|
||||
public static Field renderGlobal_chunksToUpdateForced;
|
||||
|
||||
// Config.isShaders() (Optifine only)
|
||||
public static Method config_isShaders;
|
||||
|
||||
static {
|
||||
try {
|
||||
shaders_frameTimeCounter = Class.forName("shadersmod.client.Shaders")
|
||||
.getDeclaredField("frameTimeCounter");
|
||||
shaders_frameTimeCounter.setAccessible(true);
|
||||
|
||||
shaders_isShadowPass = Class.forName("shadersmod.client.Shaders")
|
||||
.getDeclaredField("isShadowPass");
|
||||
shaders_isShadowPass.setAccessible(true);
|
||||
|
||||
shaders_beginRender = Class.forName("shadersmod.client.Shaders")
|
||||
.getDeclaredMethod("beginRender", Minecraft.class, float.class, long.class);
|
||||
shaders_beginRender.setAccessible(true);
|
||||
|
||||
renderGlobal_chunksToUpdateForced = Class.forName("net.minecraft.client.renderer.RenderGlobal")
|
||||
.getDeclaredField("chunksToUpdateForced");
|
||||
renderGlobal_chunksToUpdateForced.setAccessible(true);
|
||||
|
||||
config_isShaders = Class.forName("Config")
|
||||
.getDeclaredMethod("isShaders");
|
||||
config_isShaders.setAccessible(true);
|
||||
} catch (ClassNotFoundException ignore) {
|
||||
// no shaders mod installed
|
||||
} catch (NoSuchMethodException | NoSuchFieldException e) {
|
||||
// the method wasn't found. Has it been renamed?
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.replaymod.compat.shaders.mixin;
|
||||
|
||||
import com.replaymod.compat.shaders.ShaderReflection;
|
||||
import com.replaymod.replay.ReplayHandler;
|
||||
import com.replaymod.replay.ReplayModReplay;
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
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(EntityRenderer.class)
|
||||
public abstract class MixinShaderEntityRenderer {
|
||||
|
||||
@Inject(method = "renderWorldPass", at = @At("HEAD"))
|
||||
private void replayModCompat_updateShaderFrameTimeCounter(CallbackInfo ignore) {
|
||||
if (ReplayModReplay.instance.getReplayHandler() == null) return;
|
||||
if (ShaderReflection.shaders_frameTimeCounter == null) return;
|
||||
|
||||
ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler();
|
||||
float timestamp = replayHandler.getReplaySender().currentTimeStamp() / 1000f % 3600f;
|
||||
try {
|
||||
ShaderReflection.shaders_frameTimeCounter.set(null, timestamp);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.replaymod.compat.shaders.mixin;
|
||||
|
||||
import com.replaymod.render.hooks.EntityRendererHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.chunk.RenderChunk;
|
||||
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;
|
||||
|
||||
@Mixin(RenderChunk.class)
|
||||
public abstract class MixinShaderRenderChunk {
|
||||
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
/**
|
||||
* Changes the RenderChunk#isPlayerUpdate method that Optifine adds
|
||||
* to always return true while rendering so no chunks are being added
|
||||
* to a separate rendering queue
|
||||
*/
|
||||
@Inject(method = "isPlayerUpdate", at = @At("HEAD"), cancellable = true)
|
||||
private void replayModCompat_disableIsPlayerUpdate(CallbackInfoReturnable<Boolean> ci) {
|
||||
if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return;
|
||||
ci.setReturnValue(true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.replaymod.compat.shaders.mixin;
|
||||
|
||||
import com.replaymod.compat.shaders.ShaderReflection;
|
||||
import com.replaymod.render.hooks.EntityRendererHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.client.renderer.culling.ICamera;
|
||||
import net.minecraft.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(RenderGlobal.class)
|
||||
public abstract class MixinShaderRenderGlobal {
|
||||
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
@Shadow
|
||||
public boolean displayListEntitiesDirty;
|
||||
|
||||
@Inject(method = "setupTerrain", at = @At("HEAD"), cancellable = true)
|
||||
public void replayModCompat_setupTerrain(Entity viewEntity, double partialTicks, ICamera camera,
|
||||
int frameCount, boolean playerSpectator, CallbackInfo ci) {
|
||||
if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return;
|
||||
if (ShaderReflection.shaders_isShadowPass == null) return;
|
||||
|
||||
// when called by the shadow pass, displayListEntitiesDirty can't be set to false, as no chunk updates
|
||||
// are being processed. As it's being set to true by ChunkLoadingRenderGlobal#updateChunks, we have to
|
||||
// set it to false manually to exit the loop imposed by MixinRenderGlobal#replayModRender_setupTerrain.
|
||||
try {
|
||||
if ((boolean) ShaderReflection.shaders_isShadowPass.get(null) == true) {
|
||||
displayListEntitiesDirty = false;
|
||||
}
|
||||
} catch (IllegalAccessException ignore) {}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
|
||||
MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.recording.replaymod.json");
|
||||
MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.render.replaymod.json");
|
||||
MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.replay.replaymod.json");
|
||||
MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.compat.shaders.replaymod.json");
|
||||
|
||||
CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
|
||||
if (codeSource != null) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.util.Queue;
|
||||
|
||||
@Mod(modid = ReplayMod.MOD_ID,
|
||||
useMetadata = true,
|
||||
updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
|
||||
guiFactory = "com.replaymod.core.gui.GuiFactory")
|
||||
public class ReplayMod {
|
||||
|
||||
@@ -268,6 +269,22 @@ public class ReplayMod {
|
||||
}
|
||||
|
||||
runLater(() -> {
|
||||
// Cleanup deleted corrupted replays
|
||||
try {
|
||||
File[] files = getReplayFolder().listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory() && file.getName().endsWith(".mcpr.del")) {
|
||||
if (file.lastModified() + 2 * 24 * 60 * 60 * 1000 < System.currentTimeMillis()) {
|
||||
FileUtils.deleteDirectory(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Restore corrupted replays
|
||||
try {
|
||||
File[] files = getReplayFolder().listFiles();
|
||||
|
||||
142
src/main/java/com/replaymod/extras/OpenEyeExtra.java
Normal file
142
src/main/java/com/replaymod/extras/OpenEyeExtra.java
Normal file
@@ -0,0 +1,142 @@
|
||||
package com.replaymod.extras;
|
||||
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.core.Setting;
|
||||
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
|
||||
import de.johni0702.minecraft.gui.container.GuiContainer;
|
||||
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.function.Tickable;
|
||||
import de.johni0702.minecraft.gui.layout.CustomLayout;
|
||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||
import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
public class OpenEyeExtra implements Extra {
|
||||
private static final String DOWNLOAD_URL = "http://www.replaymod.com/dl/openeye/" + Loader.MC_VERSION;
|
||||
private static final Setting<Boolean> ASK_FOR_OPEN_EYE = new Setting<>("advanced", "askForOpenEye", null, true);
|
||||
|
||||
private ReplayMod mod;
|
||||
|
||||
@Override
|
||||
public void register(ReplayMod mod) throws Exception {
|
||||
this.mod = mod;
|
||||
mod.getSettingsRegistry().register(ASK_FOR_OPEN_EYE);
|
||||
|
||||
if (!Loader.isModLoaded("OpenEye") && mod.getSettingsRegistry().get(ASK_FOR_OPEN_EYE)) {
|
||||
mod.runLater(() -> new OfferGui(GuiScreen.wrap(mod.getMinecraft().currentScreen)).display());
|
||||
}
|
||||
}
|
||||
|
||||
private class OfferGui extends AbstractGuiScreen<OfferGui> {
|
||||
public final GuiScreen parent;
|
||||
public final GuiPanel textPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(3))
|
||||
.addElements(new VerticalLayout.Data(0.5),
|
||||
new GuiLabel().setI18nText("replaymod.gui.offeropeneye1"),
|
||||
new GuiLabel().setI18nText("replaymod.gui.offeropeneye2"),
|
||||
new GuiLabel().setI18nText("replaymod.gui.offeropeneye3"),
|
||||
new GuiLabel().setI18nText("replaymod.gui.offeropeneye4"),
|
||||
new GuiLabel().setI18nText("replaymod.gui.offeropeneye5"),
|
||||
new GuiLabel().setI18nText("replaymod.gui.offeropeneye6"),
|
||||
new GuiLabel().setI18nText("replaymod.gui.offeropeneye7"),
|
||||
new GuiLabel().setI18nText("replaymod.gui.offeropeneye8"));
|
||||
public final GuiPanel buttonPanel = new GuiPanel()
|
||||
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER).setSpacing(5));
|
||||
public final GuiPanel contentPanel = new GuiPanel(this).setLayout(new VerticalLayout().setSpacing(20))
|
||||
.addElements(new VerticalLayout.Data(0.5), textPanel, buttonPanel);
|
||||
public final GuiButton yesButton = new GuiButton(buttonPanel).setSize(150, 20).setI18nLabel("gui.yes");
|
||||
public final GuiButton noButton = new GuiButton(buttonPanel).setSize(150, 20).setI18nLabel("gui.no");
|
||||
|
||||
public OfferGui(GuiScreen parent) {
|
||||
this.parent = parent;
|
||||
|
||||
yesButton.onClick(() -> {
|
||||
GuiPopup popup = new GuiPopup(OfferGui.this);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
File targetFile = new File("mods/" + Loader.MC_VERSION, "OpenEye.jar");
|
||||
FileUtils.forceMkdir(targetFile.getParentFile());
|
||||
|
||||
ReadableByteChannel in = Channels.newChannel(new URL(DOWNLOAD_URL).openStream());
|
||||
FileChannel out = new FileOutputStream(targetFile).getChannel();
|
||||
out.transferFrom(in, 0, Long.MAX_VALUE);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
mod.runLater(() -> {
|
||||
popup.close();
|
||||
parent.display();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
});
|
||||
noButton.onClick(() -> {
|
||||
mod.getSettingsRegistry().set(ASK_FOR_OPEN_EYE, false);
|
||||
mod.getSettingsRegistry().save();
|
||||
parent.display();
|
||||
});
|
||||
|
||||
setLayout(new CustomLayout<OfferGui>() {
|
||||
@Override
|
||||
protected void layout(OfferGui container, int width, int height) {
|
||||
pos(contentPanel, width / 2 - width(contentPanel) / 2, height / 2 - height(contentPanel) / 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OfferGui getThis() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class GuiPopup extends AbstractGuiPopup<GuiPopup> {
|
||||
GuiPopup(GuiContainer container) {
|
||||
super(container);
|
||||
popup.addElements(null, new GuiIndicator().setColor(Colors.BLACK));
|
||||
setBackgroundColor(Colors.DARK_TRANSPARENT);
|
||||
open();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuiPopup getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
private static final class GuiIndicator extends GuiLabel implements Tickable {
|
||||
private int tick;
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
tick++;
|
||||
setText(new String[]{
|
||||
"Ooooo",
|
||||
"oOooo",
|
||||
"ooOoo",
|
||||
"oooOo",
|
||||
"ooooO",
|
||||
"oooOo",
|
||||
"ooOoo",
|
||||
"oOooo",
|
||||
}[tick / 5 % 8]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class ReplayModExtras {
|
||||
FullBrightness.class,
|
||||
HotkeyButtons.class,
|
||||
LocalizationExtra.class,
|
||||
VersionChecker.class
|
||||
OpenEyeExtra.class
|
||||
);
|
||||
|
||||
private Logger logger;
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.replaymod.extras;
|
||||
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.online.ReplayModOnline;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.client.event.GuiScreenEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class VersionChecker implements Extra {
|
||||
@Mod.Instance(ReplayModOnline.MOD_ID)
|
||||
private static ReplayModOnline module;
|
||||
|
||||
@Override
|
||||
public void register(ReplayMod mod) throws Exception {
|
||||
final String currentVersion = mod.getVersion();
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
boolean upToDate = module.getApiClient().isVersionUpToDate(currentVersion);
|
||||
if (!upToDate) {
|
||||
MinecraftForge.EVENT_BUS.register(VersionChecker.this);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, "ReplayMod-VersionChecker").start();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onDrawScreen(GuiScreenEvent.DrawScreenEvent.Post event) {
|
||||
if (!(event.gui instanceof GuiMainMenu)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int width = Math.max(100, event.gui.width / 2 - 100 - 10);
|
||||
|
||||
@SuppressWarnings("unchecked") List<String> lines =
|
||||
event.gui.mc.fontRendererObj.listFormattedStringToWidth(I18n.format("replaymod.gui.outdated"), width);
|
||||
|
||||
int maxLineWidth = 0;
|
||||
for(String line : lines) {
|
||||
int lineWidth = event.gui.mc.fontRendererObj.getStringWidth(line);
|
||||
if(lineWidth > maxLineWidth) {
|
||||
maxLineWidth = lineWidth;
|
||||
}
|
||||
}
|
||||
|
||||
Gui.drawRect(2, 77, 5 + maxLineWidth + 3, 80 + (lines.size() * 10), 0x80FF0000);
|
||||
|
||||
int i = 0;
|
||||
for(String line : lines) {
|
||||
event.gui.mc.fontRendererObj.drawStringWithShadow(line, 5, 80 + (i * 10), Color.WHITE.getRGB());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.replaymod.online.api.replay.pagination.FavoritedFilePagination;
|
||||
import com.replaymod.online.api.replay.pagination.Pagination;
|
||||
import com.replaymod.online.api.replay.pagination.SearchPagination;
|
||||
import com.replaymod.replaystudio.replay.ReplayMetaData;
|
||||
import com.replaymod.replaystudio.studio.ReplayStudio;
|
||||
import de.johni0702.minecraft.gui.container.AbstractGuiContainer;
|
||||
import de.johni0702.minecraft.gui.container.GuiContainer;
|
||||
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||
@@ -115,6 +116,9 @@ public class GuiReplayCenter extends GuiScreen {
|
||||
boolean disliked = dislikedReplays.contains(replayId);
|
||||
|
||||
loadButton.setI18nLabel(selected.downloaded ? "replaymod.gui.load" : "replaymod.gui.download");
|
||||
if (selected.incompatible) {
|
||||
loadButton.setDisabled();
|
||||
}
|
||||
|
||||
favoriteButton.setI18nLabel("replaymod.gui.center." + (favorited ? "unfavorite" : "favorite"));
|
||||
// Only allow button usage for either unfavorite or favorite after they've actually downloaded it
|
||||
@@ -339,6 +343,7 @@ public class GuiReplayCenter extends GuiScreen {
|
||||
public final GuiLabel author = new GuiLabel();
|
||||
public final GuiLabel date = new GuiLabel().setColor(Colors.LIGHT_GRAY);
|
||||
public final GuiLabel server = new GuiLabel().setColor(Colors.LIGHT_GRAY);
|
||||
public final GuiLabel version = new GuiLabel().setColor(Colors.RED);
|
||||
public final GuiLabel category = new GuiLabel().setColor(Colors.GREY);
|
||||
public final GuiPanel stats = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(3));
|
||||
public final GuiLabel favorites = new GuiLabel(stats).setColor(Colors.ORANGE);
|
||||
@@ -353,9 +358,10 @@ public class GuiReplayCenter extends GuiScreen {
|
||||
pos(category, 0, y(server) + height(server) + 3);
|
||||
|
||||
pos(date, width - width(date), y(author));
|
||||
pos(version, width - width(version), y(server));
|
||||
pos(stats, width - width(stats), y(category));
|
||||
}
|
||||
}).addElements(null, name, author, date, server, category, stats);
|
||||
}).addElements(null, name, author, date, server, version, category, stats);
|
||||
public final GuiImage thumbnail;
|
||||
public final GuiLabel duration = new GuiLabel();
|
||||
public final GuiPanel durationPanel = new GuiPanel().setBackgroundColor(Colors.HALF_TRANSPARENT)
|
||||
@@ -389,6 +395,7 @@ public class GuiReplayCenter extends GuiScreen {
|
||||
private final long dateMillis;
|
||||
private final int sortId;
|
||||
private final boolean downloaded;
|
||||
private final boolean incompatible;
|
||||
|
||||
public GuiReplayEntry(FileInfo fileInfo, BufferedImage thumbImage, int sortId, boolean downloaded) {
|
||||
this.fileInfo = fileInfo;
|
||||
@@ -404,6 +411,10 @@ public class GuiReplayCenter extends GuiScreen {
|
||||
} else {
|
||||
server.setText(metaData.getServerName());
|
||||
}
|
||||
incompatible = !new ReplayStudio().isCompatible(fileInfo.getMetadata().getFileFormatVersion());
|
||||
if (incompatible) {
|
||||
version.setText("Minecraft " + fileInfo.getMetadata().getMcVersion());
|
||||
}
|
||||
dateMillis = metaData.getDate();
|
||||
date.setText(new SimpleDateFormat().format(new Date(dateMillis)));
|
||||
if (thumbImage == null) {
|
||||
|
||||
@@ -11,15 +11,18 @@ import de.johni0702.minecraft.gui.element.GuiLabel;
|
||||
import de.johni0702.minecraft.gui.element.GuiTextField;
|
||||
import de.johni0702.minecraft.gui.element.GuiToggleButton;
|
||||
import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu;
|
||||
import de.johni0702.minecraft.gui.function.Typeable;
|
||||
import de.johni0702.minecraft.gui.layout.GridLayout;
|
||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||
import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.util.ReadablePoint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GuiReplayCenterSearch extends AbstractGuiPopup<GuiReplayCenterSearch> {
|
||||
public class GuiReplayCenterSearch extends AbstractGuiPopup<GuiReplayCenterSearch> implements Typeable {
|
||||
private final GuiReplayCenter replayCenter;
|
||||
private final ApiClient apiClient;
|
||||
public final GuiLabel title = new GuiLabel().setI18nText("replaymod.gui.center.search.filters").setColor(Colors.BLACK);
|
||||
@@ -100,4 +103,13 @@ public class GuiReplayCenterSearch extends AbstractGuiPopup<GuiReplayCenterSearc
|
||||
protected GuiReplayCenterSearch getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) {
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
cancelButton.onClick();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ public abstract class AbstractTimelinePlayer {
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
private final ReplayHandler replayHandler;
|
||||
private Timeline timeline;
|
||||
protected long startOffset;
|
||||
private long lastTime;
|
||||
private long lastTimestamp;
|
||||
private ListenableFuture<Void> future;
|
||||
@@ -33,6 +34,11 @@ public abstract class AbstractTimelinePlayer {
|
||||
this.replayHandler = replayHandler;
|
||||
}
|
||||
|
||||
public ListenableFuture<Void> start(Timeline timeline, long from) {
|
||||
startOffset = from;
|
||||
return start(timeline);
|
||||
}
|
||||
|
||||
public ListenableFuture<Void> start(Timeline timeline) {
|
||||
this.timeline = timeline;
|
||||
|
||||
@@ -79,6 +85,7 @@ public abstract class AbstractTimelinePlayer {
|
||||
public void onTick(ReplayTimer.UpdatedEvent event) {
|
||||
if (future.isDone()) {
|
||||
mc.timer = ((ReplayTimer) mc.timer).getWrapped();
|
||||
replayHandler.getReplaySender().setReplaySpeed(0);
|
||||
replayHandler.getReplaySender().setAsyncMode(true);
|
||||
FMLCommonHandler.instance().bus().unregister(this);
|
||||
return;
|
||||
|
||||
@@ -46,6 +46,6 @@ public class RealtimeTimelinePlayer extends AbstractTimelinePlayer {
|
||||
|
||||
@Override
|
||||
public long getTimePassed() {
|
||||
return firstFrame ? 0 : System.currentTimeMillis() - startTime;
|
||||
return startOffset + (firstFrame ? 0 : System.currentTimeMillis() - startTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.replaymod.recording;
|
||||
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.core.utils.Restrictions;
|
||||
import com.replaymod.recording.handler.ConnectionEventHandler;
|
||||
import com.replaymod.recording.packet.PacketListener;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
@@ -8,6 +9,7 @@ import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventBus;
|
||||
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
@@ -44,5 +46,7 @@ public class ReplayModRecording {
|
||||
public void init(FMLInitializationEvent event) {
|
||||
EventBus bus = FMLCommonHandler.instance().bus();
|
||||
bus.register(connectionEventHandler = new ConnectionEventHandler(logger, core));
|
||||
|
||||
NetworkRegistry.INSTANCE.newSimpleChannel(Restrictions.PLUGIN_CHANNEL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.crash.CrashReportCategory;
|
||||
import org.apache.commons.exec.CommandLine;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.output.TeeOutputStream;
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
@@ -34,6 +35,7 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
|
||||
this.settings = settings;
|
||||
|
||||
File outputFolder = settings.getOutputFile().getParentFile();
|
||||
FileUtils.forceMkdir(outputFolder);
|
||||
String fileName = settings.getOutputFile().getName();
|
||||
|
||||
commandArgs = settings.getExportArguments()
|
||||
|
||||
@@ -6,11 +6,11 @@ import com.replaymod.render.frame.ODSOpenGlFrame;
|
||||
import com.replaymod.render.frame.OpenGlFrame;
|
||||
import com.replaymod.render.rendering.FrameCapturer;
|
||||
import com.replaymod.render.shader.Program;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.util.ReportedException;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.util.Dimension;
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -31,6 +31,8 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
||||
private final BooleanState[] previousStates = new BooleanState[3];
|
||||
private final BooleanState previousFogState;
|
||||
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
public ODSFrameCapturer(WorldRenderer worldRenderer, final RenderInfo renderInfo, int frameSize) {
|
||||
RenderInfo fakeInfo = new RenderInfo() {
|
||||
private int call;
|
||||
@@ -143,6 +145,8 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
||||
|
||||
@Override
|
||||
protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) {
|
||||
resize(getFrameWidth(), getFrameHeight());
|
||||
|
||||
pushMatrix();
|
||||
frameBuffer().bindFramebuffer(true);
|
||||
|
||||
@@ -150,7 +154,7 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
||||
enableTexture2D();
|
||||
|
||||
directionVariable.set(captureData.ordinal());
|
||||
worldRenderer.renderWorld(new Dimension(getFrameWidth(), getFrameHeight()), partialTicks, null);
|
||||
worldRenderer.renderWorld(partialTicks, null);
|
||||
|
||||
frameBuffer().unbindFramebuffer();
|
||||
popMatrix();
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.replaymod.render.frame.OpenGlFrame;
|
||||
import com.replaymod.render.rendering.Frame;
|
||||
import com.replaymod.render.rendering.FrameCapturer;
|
||||
import com.replaymod.render.utils.ByteBufferPool;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.shader.Framebuffer;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -24,6 +25,8 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
|
||||
protected int framesDone;
|
||||
private Framebuffer frameBuffer;
|
||||
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
public OpenGlFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo) {
|
||||
this.worldRenderer = worldRenderer;
|
||||
this.renderInfo = renderInfo;
|
||||
@@ -56,7 +59,7 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
|
||||
|
||||
protected Framebuffer frameBuffer() {
|
||||
if (frameBuffer == null) {
|
||||
frameBuffer = new Framebuffer(getFrameWidth(), getFrameHeight(), true);
|
||||
frameBuffer = Minecraft.getMinecraft().getFramebuffer();
|
||||
}
|
||||
return frameBuffer;
|
||||
}
|
||||
@@ -71,13 +74,15 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
|
||||
}
|
||||
|
||||
protected OpenGlFrame renderFrame(int frameId, float partialTicks, D captureData) {
|
||||
resize(getFrameWidth(), getFrameHeight());
|
||||
|
||||
pushMatrix();
|
||||
frameBuffer().bindFramebuffer(true);
|
||||
|
||||
clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
enableTexture2D();
|
||||
|
||||
worldRenderer.renderWorld(frameSize, partialTicks, captureData);
|
||||
worldRenderer.renderWorld(partialTicks, captureData);
|
||||
|
||||
frameBuffer().unbindFramebuffer();
|
||||
popMatrix();
|
||||
@@ -102,11 +107,18 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
|
||||
return new OpenGlFrame(frameId, new Dimension(getFrameWidth(), getFrameHeight()), buffer);
|
||||
}
|
||||
|
||||
protected void resize(int width, int height) {
|
||||
if (width != mc.displayWidth || height != mc.displayHeight) {
|
||||
setWindowSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
private void setWindowSize(int width, int height) {
|
||||
mc.resize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
worldRenderer.close();
|
||||
if (frameBuffer != null) {
|
||||
frameBuffer.deleteFramebuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.replaymod.render.capturer;
|
||||
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
public interface WorldRenderer extends Closeable {
|
||||
void renderWorld(ReadableDimension displaySize, float partialTicks, CaptureData data);
|
||||
void renderWorld(float partialTicks, CaptureData data);
|
||||
void setOmnidirectional(boolean omnidirectional);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
||||
}).setSize(122, 20).setSteps(110);
|
||||
public final GuiPanel videoResolutionPanel = new GuiPanel()
|
||||
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(2))
|
||||
.addElements(null, videoWidth, new GuiLabel().setText("*"), videoHeight);
|
||||
.addElements(new HorizontalLayout.Data(0.5), videoWidth, new GuiLabel().setText("*"), videoHeight);
|
||||
|
||||
public final GuiNumberField bitRateField = new GuiNumberField().setValue(10).setSize(50, 20);
|
||||
public final GuiDropdownMenu<String> bitRateUnit = new GuiDropdownMenu<String>()
|
||||
@@ -130,7 +130,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
||||
new GuiPanel().addElements(null, bitRateField, bitRateUnit).setLayout(new HorizontalLayout()),
|
||||
frameRateSlider).setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(3)),
|
||||
new GuiLabel().setI18nText("replaymod.gui.rendersettings.outputfile"), outputFileButton)
|
||||
.setLayout(new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(3));
|
||||
.setLayout(new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(5));
|
||||
|
||||
public final GuiCheckbox nametagCheckbox = new GuiCheckbox()
|
||||
.setI18nLabel("replaymod.gui.rendersettings.nametags");
|
||||
@@ -186,7 +186,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
||||
// Closing this GUI ensures that settings are saved
|
||||
getMinecraft().displayGuiScreen(null);
|
||||
try {
|
||||
VideoRenderer videoRenderer = new VideoRenderer(save(true), replayHandler, timeline);
|
||||
VideoRenderer videoRenderer = new VideoRenderer(save(false), replayHandler, timeline);
|
||||
videoRenderer.renderVideo();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@@ -284,6 +284,15 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
||||
videoHeight.setTextColor(Colors.RED);
|
||||
}
|
||||
|
||||
// Enable/Disable bitrate input field and dropdown
|
||||
if (encodingPresetDropdown.getSelectedValue().hasBitrateSetting()) {
|
||||
bitRateField.setEnabled();
|
||||
bitRateUnit.setEnabled();
|
||||
} else {
|
||||
bitRateField.setDisabled();
|
||||
bitRateUnit.setDisabled();
|
||||
}
|
||||
|
||||
// Enable/Disable camera stabilization checkboxes
|
||||
switch (renderMethodDropdown.getSelectedValue()) {
|
||||
case CUBIC:
|
||||
@@ -378,7 +387,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
||||
updateInputs();
|
||||
}
|
||||
|
||||
public RenderSettings save(boolean saveOutputFile) {
|
||||
public RenderSettings save(boolean serialize) {
|
||||
return new RenderSettings(
|
||||
renderMethodDropdown.getSelectedValue(),
|
||||
encodingPresetDropdown.getSelectedValue(),
|
||||
@@ -386,13 +395,13 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
||||
videoHeight.getInteger(),
|
||||
frameRateSlider.getValue() + 10,
|
||||
bitRateField.getInteger() << (10 * bitRateUnit.getSelected()),
|
||||
saveOutputFile ? outputFile : null,
|
||||
serialize ? null : outputFile,
|
||||
nametagCheckbox.isChecked(),
|
||||
stabilizeYaw.isChecked(),
|
||||
stabilizePitch.isChecked(),
|
||||
stabilizeRoll.isChecked(),
|
||||
stabilizeYaw.isChecked() && (serialize || stabilizeYaw.isEnabled()),
|
||||
stabilizePitch.isChecked() && (serialize || stabilizePitch.isEnabled()),
|
||||
stabilizeRoll.isChecked() && (serialize || stabilizeRoll.isEnabled()),
|
||||
chromaKeyingCheckbox.isChecked() ? chromaKeyingColor.getColor() : null,
|
||||
inject360Metadata.isChecked(),
|
||||
inject360Metadata.isChecked() && (serialize || inject360Metadata.isEnabled()),
|
||||
exportCommand.getText(),
|
||||
exportArguments.getText(),
|
||||
net.minecraft.client.gui.GuiScreen.isCtrlKeyDown()
|
||||
@@ -412,7 +421,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
RenderSettings settings = save(false);
|
||||
RenderSettings settings = save(true);
|
||||
String json = new Gson().toJson(settings);
|
||||
Configuration config = ReplayModRender.instance.getConfiguration();
|
||||
getConfigProperty(config).set(json);
|
||||
|
||||
@@ -14,7 +14,6 @@ import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
|
||||
import de.johni0702.minecraft.gui.layout.CustomLayout;
|
||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.util.Dimension;
|
||||
@@ -23,8 +22,6 @@ import org.lwjgl.util.ReadablePoint;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static net.minecraft.client.renderer.GlStateManager.bindTexture;
|
||||
|
||||
public class GuiVideoRenderer extends GuiScreen {
|
||||
private static final ResourceLocation NO_PREVIEW_TEXTURE = new ResourceLocation("replaymod", "logo.jpg");
|
||||
|
||||
@@ -232,13 +229,7 @@ public class GuiVideoRenderer extends GuiScreen {
|
||||
final int videoHeight = videoSize.getHeight();
|
||||
|
||||
if (previewTexture == null) {
|
||||
previewTexture = new DynamicTexture(videoWidth, videoHeight) {
|
||||
@Override
|
||||
public void updateDynamicTexture() {
|
||||
bindTexture(getGlTextureId());
|
||||
TextureUtil.uploadTextureSub(0, getTextureData(), videoWidth, videoHeight, 0, 0, true, false, false);
|
||||
}
|
||||
};
|
||||
previewTexture = new DynamicTexture(videoWidth, videoHeight);
|
||||
}
|
||||
|
||||
if (previewTextureDirty) {
|
||||
@@ -273,7 +264,10 @@ public class GuiVideoRenderer extends GuiScreen {
|
||||
buffer.mark();
|
||||
synchronized (this) {
|
||||
int[] data = previewTexture.getTextureData();
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
// Optifine changes the texture data array to be three times as long (for use by shaders),
|
||||
// we only want to initialize the first third which is why we use the length of the buffer instead
|
||||
// of the length of the data array
|
||||
for (int i = 0; buffer.remaining() > 0; i++) {
|
||||
data[i] = 0xff << 24 | (buffer.get() & 0xff) << 16 | (buffer.get() & 0xff) << 8 | (buffer.get() & 0xff);
|
||||
}
|
||||
previewTextureDirty = true;
|
||||
|
||||
@@ -5,14 +5,11 @@ import com.replaymod.render.capturer.CaptureData;
|
||||
import com.replaymod.render.capturer.WorldRenderer;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.culling.ClippingHelper;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static net.minecraft.client.renderer.GlStateManager.*;
|
||||
|
||||
public class EntityRendererHandler implements WorldRenderer {
|
||||
public final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
@@ -29,27 +26,10 @@ public class EntityRendererHandler implements WorldRenderer {
|
||||
((IEntityRenderer) mc.entityRenderer).replayModRender_setHandler(this);
|
||||
}
|
||||
|
||||
public void withDisplaySize(int displayWidth, int displayHeight, Runnable runnable) {
|
||||
final int prevWidth = mc.displayWidth;
|
||||
final int prevHeight = mc.displayHeight;
|
||||
mc.displayWidth = displayWidth;
|
||||
mc.displayHeight = displayHeight;
|
||||
|
||||
runnable.run();
|
||||
|
||||
mc.displayWidth = prevWidth;
|
||||
mc.displayHeight = prevHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWorld(ReadableDimension displaySize, final float partialTicks, CaptureData data) {
|
||||
public void renderWorld(final float partialTicks, CaptureData data) {
|
||||
this.data = data;
|
||||
withDisplaySize(displaySize.getWidth(), displaySize.getHeight(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
renderWorld(partialTicks, 0);
|
||||
}
|
||||
});
|
||||
renderWorld(partialTicks, 0);
|
||||
}
|
||||
|
||||
public void renderWorld(float partialTicks, long finishTimeNano) {
|
||||
@@ -57,9 +37,9 @@ public class EntityRendererHandler implements WorldRenderer {
|
||||
|
||||
mc.entityRenderer.updateLightmap(partialTicks);
|
||||
|
||||
enableDepth();
|
||||
enableAlpha();
|
||||
alphaFunc(516, 0.5F);
|
||||
GlStateManager.enableDepth();
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.alphaFunc(516, 0.5F);
|
||||
|
||||
mc.entityRenderer.renderWorldPass(2, partialTicks, finishTimeNano);
|
||||
|
||||
@@ -76,13 +56,6 @@ public class EntityRendererHandler implements WorldRenderer {
|
||||
this.omnidirectional = omnidirectional;
|
||||
}
|
||||
|
||||
public static final class NoCullingClippingHelper extends ClippingHelper {
|
||||
@Override
|
||||
public boolean isBoxInFrustum(double p_78553_1_, double p_78553_3_, double p_78553_5_, double p_78553_7_, double p_78553_9_, double p_78553_11_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public interface GluPerspective {
|
||||
void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -49,14 +48,6 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/culling/Frustum;setPosition(DDD)V"))
|
||||
public void replayModRender_createNoCullingFrustum(Frustum frustum, double x, double y, double z) {
|
||||
if (replayModRender_handler != null) {
|
||||
frustum.clippingHelper = new EntityRendererHandler.NoCullingClippingHelper();
|
||||
}
|
||||
frustum.setPosition(x, y, z);
|
||||
}
|
||||
|
||||
@Inject(method = "orientCamera", at = @At("HEAD"))
|
||||
private void replayModRender_resetRotationIfNeeded(float partialTicks, CallbackInfo ci) {
|
||||
if (replayModRender_handler != null) {
|
||||
|
||||
@@ -39,6 +39,8 @@ public abstract class MixinRenderGlobal {
|
||||
setupTerrain(viewEntity, partialTicks, camera, replayModRender_hook.nextFrameId(), playerSpectator);
|
||||
} while (displayListEntitiesDirty);
|
||||
|
||||
displayListEntitiesDirty = true;
|
||||
|
||||
replayModRender_passThroughSetupTerrain = false;
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.SoundCategory;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.shader.Framebuffer;
|
||||
import net.minecraft.util.Timer;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.Display;
|
||||
@@ -62,6 +63,9 @@ public class VideoRenderer implements RenderInfo {
|
||||
private boolean paused;
|
||||
private boolean cancelled;
|
||||
|
||||
private Framebuffer guiFramebuffer;
|
||||
private int displayWidth, displayHeight;
|
||||
|
||||
public VideoRenderer(RenderSettings settings, ReplayHandler replayHandler, Timeline timeline) throws IOException {
|
||||
this.settings = settings;
|
||||
this.replayHandler = replayHandler;
|
||||
@@ -130,6 +134,13 @@ public class VideoRenderer implements RenderInfo {
|
||||
|
||||
@Override
|
||||
public float updateForNextFrame() {
|
||||
// because the jGui lib uses Minecraft's displayWidth and displayHeight values, update these temporarily
|
||||
int displayWidthBefore = mc.displayWidth;
|
||||
int displayHeightBefore = mc.displayHeight;
|
||||
|
||||
mc.displayWidth = displayWidth;
|
||||
mc.displayHeight = displayHeight;
|
||||
|
||||
if (!settings.isHighPerformance() || framesDone % fps == 0) {
|
||||
drawGui();
|
||||
}
|
||||
@@ -142,6 +153,10 @@ public class VideoRenderer implements RenderInfo {
|
||||
tick();
|
||||
}
|
||||
|
||||
// change Minecraft's display size back
|
||||
mc.displayWidth = displayWidthBefore;
|
||||
mc.displayHeight = displayHeightBefore;
|
||||
|
||||
framesDone++;
|
||||
return mc.timer.renderPartialTicks;
|
||||
}
|
||||
@@ -192,10 +207,15 @@ public class VideoRenderer implements RenderInfo {
|
||||
|
||||
totalFrames = (int) (duration*fps/1000);
|
||||
|
||||
ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||
updateDisplaySize();
|
||||
|
||||
ScaledResolution scaled = new ScaledResolution(mc, displayWidth, displayHeight);
|
||||
gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight());
|
||||
|
||||
chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.renderGlobal);
|
||||
|
||||
// Set up our own framebuffer to render the GUI to
|
||||
guiFramebuffer = new Framebuffer(displayWidth, displayHeight, true);
|
||||
}
|
||||
|
||||
private void finish() {
|
||||
@@ -219,6 +239,9 @@ public class VideoRenderer implements RenderInfo {
|
||||
ReplayMod.soundHandler.playRenderSuccessSound();
|
||||
|
||||
new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();
|
||||
|
||||
// Finally, resize the Minecraft framebuffer to the actual width/height of the window
|
||||
mc.resize(displayWidth, displayHeight);
|
||||
}
|
||||
|
||||
private void tick() {
|
||||
@@ -238,12 +261,22 @@ public class VideoRenderer implements RenderInfo {
|
||||
|
||||
public void drawGui() {
|
||||
do {
|
||||
// Resize the GUI framebuffer if the display size changed
|
||||
if (!settings.isHighPerformance() && displaySizeChanged()) {
|
||||
updateDisplaySize();
|
||||
guiFramebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight);
|
||||
}
|
||||
|
||||
pushMatrix();
|
||||
clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
enableTexture2D();
|
||||
mc.getFramebuffer().bindFramebuffer(true);
|
||||
guiFramebuffer.bindFramebuffer(true);
|
||||
|
||||
mc.entityRenderer.setupOverlayRendering();
|
||||
|
||||
ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||
gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight());
|
||||
|
||||
try {
|
||||
gui.toMinecraft().handleInput();
|
||||
} catch (IOException e) {
|
||||
@@ -252,18 +285,17 @@ public class VideoRenderer implements RenderInfo {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||
int mouseX = Mouse.getX() * scaled.getScaledWidth() / mc.displayWidth;
|
||||
int mouseY = scaled.getScaledHeight() - Mouse.getY() * scaled.getScaledHeight() / mc.displayHeight - 1;
|
||||
|
||||
gui.toMinecraft().drawScreen(mouseX, mouseY, 0);
|
||||
|
||||
mc.getFramebuffer().unbindFramebuffer();
|
||||
guiFramebuffer.unbindFramebuffer();
|
||||
popMatrix();
|
||||
pushMatrix();
|
||||
mc.getFramebuffer().framebufferRender(mc.displayWidth, mc.displayHeight);
|
||||
guiFramebuffer.framebufferRender(displayWidth, displayHeight);
|
||||
popMatrix();
|
||||
|
||||
|
||||
// if not in high performance mode, update the gui size if screen size changed
|
||||
// otherwise just swap the progress gui to screen
|
||||
if (settings.isHighPerformance()) {
|
||||
@@ -285,6 +317,15 @@ public class VideoRenderer implements RenderInfo {
|
||||
} while (paused);
|
||||
}
|
||||
|
||||
private boolean displaySizeChanged() {
|
||||
return displayWidth != Display.getWidth() || displayHeight != Display.getHeight();
|
||||
}
|
||||
|
||||
private void updateDisplaySize() {
|
||||
displayWidth = Display.getWidth();
|
||||
displayHeight = Display.getHeight();
|
||||
}
|
||||
|
||||
public int getFramesDone() {
|
||||
return framesDone;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class ReplayHandler {
|
||||
*/
|
||||
private boolean suppressCameraMovements;
|
||||
|
||||
private final Set<Marker> markers;
|
||||
private final List<Marker> markers;
|
||||
|
||||
private final GuiReplayOverlay overlay;
|
||||
|
||||
@@ -78,7 +78,7 @@ public class ReplayHandler {
|
||||
|
||||
FMLCommonHandler.instance().bus().post(new ReplayOpenEvent.Pre(this));
|
||||
|
||||
markers = new HashSet<>(replayFile.getMarkers().or(Collections.<Marker>emptySet()));
|
||||
markers = new ArrayList<>(replayFile.getMarkers().or(Collections.emptySet()));
|
||||
|
||||
replaySender = new ReplaySender(this, replayFile, asyncMode);
|
||||
|
||||
@@ -185,9 +185,9 @@ public class ReplayHandler {
|
||||
/**
|
||||
* Returns all markers.
|
||||
* When changed, {@link #saveMarkers()} should be called to save the changes.
|
||||
* @return Set of markers
|
||||
* @return Collection of markers in no particular order
|
||||
*/
|
||||
public Set<Marker> getMarkers() {
|
||||
public Collection<Marker> getMarkers() {
|
||||
return markers;
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ public class ReplayHandler {
|
||||
*/
|
||||
public void saveMarkers() {
|
||||
try {
|
||||
replayFile.writeMarkers(markers);
|
||||
replayFile.writeMarkers(new HashSet<>(markers));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.AbstractClientPlayer;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
@@ -237,6 +238,14 @@ public class CameraEntity extends EntityPlayerSP {
|
||||
return ReplayModReplay.instance.getReplayHandler().isCameraView(); // Make sure we're treated as spectator
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderInPass(int pass) {
|
||||
// Never render the camera
|
||||
// This is necessary to hide the player head in third person mode and to not
|
||||
// cause any unwanted shadows when rendering with shaders.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getLocationSkin() {
|
||||
Entity view = mc.getRenderViewEntity();
|
||||
@@ -246,6 +255,15 @@ public class CameraEntity extends EntityPlayerSP {
|
||||
return super.getLocationSkin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSkinType() {
|
||||
Entity view = mc.getRenderViewEntity();
|
||||
if (view != this && view instanceof AbstractClientPlayer) {
|
||||
return ((AbstractClientPlayer) view).getSkinType();
|
||||
}
|
||||
return super.getSkinType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSwingProgress(float renderPartialTicks) {
|
||||
Entity view = mc.getRenderViewEntity();
|
||||
|
||||
@@ -5,14 +5,17 @@ import com.replaymod.replay.ReplayHandler;
|
||||
import de.johni0702.minecraft.gui.container.GuiContainer;
|
||||
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||
import de.johni0702.minecraft.gui.element.*;
|
||||
import de.johni0702.minecraft.gui.function.Typeable;
|
||||
import de.johni0702.minecraft.gui.layout.GridLayout;
|
||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||
import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import com.replaymod.replaystudio.data.Marker;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.util.ReadablePoint;
|
||||
|
||||
public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> {
|
||||
public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> implements Typeable {
|
||||
private final ReplayHandler replayHandler;
|
||||
private final Marker marker;
|
||||
|
||||
@@ -107,4 +110,13 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> {
|
||||
protected GuiEditMarkerPopup getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) {
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
cancelButton.onClick();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +133,8 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline<GuiMarkerTimeline> im
|
||||
replayHandler.doJump(marker.getTime(), false);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
selectedMarker = null;
|
||||
}
|
||||
return super.mouseClick(position, button);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ import de.johni0702.minecraft.gui.GuiRenderer;
|
||||
import de.johni0702.minecraft.gui.RenderInfo;
|
||||
import de.johni0702.minecraft.gui.container.AbstractGuiOverlay;
|
||||
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||
import de.johni0702.minecraft.gui.element.GuiElement;
|
||||
import de.johni0702.minecraft.gui.element.GuiSlider;
|
||||
import de.johni0702.minecraft.gui.element.GuiTexturedButton;
|
||||
import de.johni0702.minecraft.gui.element.GuiTooltip;
|
||||
import de.johni0702.minecraft.gui.element.advanced.IGuiTimeline;
|
||||
import de.johni0702.minecraft.gui.layout.CustomLayout;
|
||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||
@@ -27,8 +29,20 @@ public class GuiReplayOverlay extends AbstractGuiOverlay<GuiReplayOverlay> {
|
||||
|
||||
public final GuiPanel topPanel = new GuiPanel(this)
|
||||
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.LEFT).setSpacing(5));
|
||||
public final GuiTexturedButton playPauseButton = new GuiTexturedButton().setSize(20, 20)
|
||||
.setTexture(ReplayMod.TEXTURE, TEXTURE_SIZE);
|
||||
public final GuiTexturedButton playPauseButton = new GuiTexturedButton() {
|
||||
@Override
|
||||
public GuiElement getTooltip(RenderInfo renderInfo) {
|
||||
GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo);
|
||||
if (tooltip != null) {
|
||||
if (getTextureNormal().getY() == 0) { // Play button
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.unpause");
|
||||
} else { // Pause button
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.pause");
|
||||
}
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
}.setSize(20, 20).setTexture(ReplayMod.TEXTURE, TEXTURE_SIZE).setTooltip(new GuiTooltip());
|
||||
public final GuiSlider speedSlider = new GuiSlider().setSize(100, 20).setSteps(37); // 0.0 is not included
|
||||
public final GuiMarkerTimeline timeline;
|
||||
|
||||
|
||||
@@ -6,7 +6,12 @@ import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.mojang.realmsclient.gui.ChatFormatting;
|
||||
import com.replaymod.core.gui.GuiReplaySettings;
|
||||
import com.replaymod.core.utils.Utils;
|
||||
import com.replaymod.replay.ReplayModReplay;
|
||||
import com.replaymod.replaystudio.replay.ReplayFile;
|
||||
import com.replaymod.replaystudio.replay.ReplayMetaData;
|
||||
import com.replaymod.replaystudio.replay.ZipReplayFile;
|
||||
import com.replaymod.replaystudio.studio.ReplayStudio;
|
||||
import de.johni0702.minecraft.gui.container.AbstractGuiContainer;
|
||||
import de.johni0702.minecraft.gui.container.GuiContainer;
|
||||
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||
@@ -20,11 +25,6 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
|
||||
import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import de.johni0702.minecraft.gui.utils.Consumer;
|
||||
import com.replaymod.replaystudio.replay.ReplayFile;
|
||||
import com.replaymod.replaystudio.replay.ReplayMetaData;
|
||||
import com.replaymod.replaystudio.replay.ZipReplayFile;
|
||||
import com.replaymod.replaystudio.studio.ReplayStudio;
|
||||
import com.replaymod.core.utils.Utils;
|
||||
import net.minecraft.client.gui.GuiErrorScreen;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.Util;
|
||||
@@ -54,6 +54,9 @@ public class GuiReplayViewer extends GuiScreen {
|
||||
@Override
|
||||
public void run() {
|
||||
replayButtonPanel.forEach(IGuiButton.class).setEnabled(list.getSelected() != null);
|
||||
if (list.getSelected() != null && list.getSelected().incompatible) {
|
||||
loadButton.setDisabled();
|
||||
}
|
||||
}
|
||||
}).onLoad(new Consumer<Consumer<Supplier<GuiReplayEntry>>> () {
|
||||
@Override
|
||||
@@ -95,6 +98,12 @@ public class GuiReplayViewer extends GuiScreen {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).onSelectionDoubleClicked(() -> {
|
||||
if (this.loadButton.isEnabled()) {
|
||||
this.loadButton.onClick();
|
||||
// Disable load button to prevent the player from opening the replay twice at the same time
|
||||
this.loadButton.setDisabled();
|
||||
}
|
||||
}).setDrawShadow(true).setDrawSlider(true);
|
||||
|
||||
public final GuiButton loadButton = new GuiButton().onClick(new Runnable() {
|
||||
@@ -270,6 +279,7 @@ public class GuiReplayViewer extends GuiScreen {
|
||||
public final GuiLabel date = new GuiLabel().setColor(Colors.LIGHT_GRAY);
|
||||
public final GuiPanel infoPanel = new GuiPanel(this).setLayout(new VerticalLayout().setSpacing(2))
|
||||
.addElements(null, name, server, date);
|
||||
public final GuiLabel version = new GuiLabel(this).setColor(Colors.RED);
|
||||
public final GuiImage thumbnail;
|
||||
public final GuiLabel duration = new GuiLabel();
|
||||
public final GuiPanel durationPanel = new GuiPanel().setBackgroundColor(Colors.HALF_TRANSPARENT)
|
||||
@@ -287,6 +297,7 @@ public class GuiReplayViewer extends GuiScreen {
|
||||
});
|
||||
|
||||
private final long dateMillis;
|
||||
private final boolean incompatible;
|
||||
|
||||
public GuiReplayEntry(File file, ReplayMetaData metaData, BufferedImage thumbImage) {
|
||||
this.file = file;
|
||||
@@ -297,6 +308,10 @@ public class GuiReplayViewer extends GuiScreen {
|
||||
} else {
|
||||
server.setText(metaData.getServerName());
|
||||
}
|
||||
incompatible = !new ReplayStudio().isCompatible(metaData.getFileFormatVersion());
|
||||
if (incompatible) {
|
||||
version.setText("Minecraft " + metaData.getMcVersion());
|
||||
}
|
||||
dateMillis = metaData.getDate();
|
||||
date.setText(new SimpleDateFormat().format(new Date(dateMillis)));
|
||||
if (thumbImage == null) {
|
||||
@@ -316,6 +331,7 @@ public class GuiReplayViewer extends GuiScreen {
|
||||
y(durationPanel, height(thumbnail) - height(durationPanel));
|
||||
|
||||
pos(infoPanel, width(thumbnail) + 5, 0);
|
||||
pos(version, width - width(version), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -84,6 +84,9 @@ public class ReplayModSimplePathing implements PathingRegistry {
|
||||
}
|
||||
|
||||
public void setCurrentTimeline(Timeline currentTimeline) {
|
||||
if (this.currentTimeline != currentTimeline) {
|
||||
selectedKeyframe = null;
|
||||
}
|
||||
this.currentTimeline = currentTimeline;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,16 +13,20 @@ import de.johni0702.minecraft.gui.element.GuiButton;
|
||||
import de.johni0702.minecraft.gui.element.GuiLabel;
|
||||
import de.johni0702.minecraft.gui.element.GuiNumberField;
|
||||
import de.johni0702.minecraft.gui.element.IGuiLabel;
|
||||
import de.johni0702.minecraft.gui.function.Typeable;
|
||||
import de.johni0702.minecraft.gui.layout.GridLayout;
|
||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||
import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import de.johni0702.minecraft.gui.utils.Consumer;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.util.ReadablePoint;
|
||||
|
||||
import static de.johni0702.minecraft.gui.utils.Utils.link;
|
||||
|
||||
public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends AbstractGuiPopup<T> {
|
||||
public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends AbstractGuiPopup<T> implements Typeable {
|
||||
private static GuiNumberField newGuiNumberField() {
|
||||
return new GuiNumberField().setPrecision(0).setValidateOnFocusChange(true);
|
||||
}
|
||||
@@ -68,9 +72,10 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
|
||||
this.path = path;
|
||||
|
||||
long time = keyframe.getTime();
|
||||
timeMinField.setValue(time / 1000 / 60);
|
||||
timeSecField.setValue(time / 1000 % 60);
|
||||
timeMSecField.setValue(time % 1000);
|
||||
Consumer<String> updateSaveButtonState = s -> saveButton.setEnabled(canSave());
|
||||
timeMinField.setValue(time / 1000 / 60).onTextChanged(updateSaveButtonState);
|
||||
timeSecField.setValue(time / 1000 % 60).onTextChanged(updateSaveButtonState);
|
||||
timeMSecField.setValue(time % 1000).onTextChanged(updateSaveButtonState);
|
||||
|
||||
title.setI18nText("replaymod.gui.editkeyframe.title." + type);
|
||||
saveButton.onClick(() -> {
|
||||
@@ -84,6 +89,23 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
|
||||
});
|
||||
}
|
||||
|
||||
private boolean canSave() {
|
||||
long newTime = (timeMinField.getInteger() * 60 + timeSecField.getInteger()) * 1000 + timeMSecField.getInteger();
|
||||
if (newTime != keyframe.getTime() && path.getKeyframe(newTime) != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) {
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
cancelButton.onClick();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
super.open();
|
||||
|
||||
@@ -121,24 +121,42 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline
|
||||
|| !segment.getInterpolator().getKeyframeProperties().contains(SpectatorProperty.PROPERTY)) {
|
||||
continue; // Not a spectator segment
|
||||
}
|
||||
long startFrameTime = segment.getStartKeyframe().getTime();
|
||||
long endFrameTime = segment.getEndKeyframe().getTime();
|
||||
if (startFrameTime >= endTime || endFrameTime <= startTime) {
|
||||
continue; // Segment out of display range
|
||||
}
|
||||
drawQuadOnSegment(renderer, visibleWidth, segment, BORDER_TOP + 1, 0xFF0088FF);
|
||||
}
|
||||
|
||||
double relativeStart = startFrameTime - startTime;
|
||||
double relativeEnd = endFrameTime - startTime;
|
||||
int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + KEYFRAME_SIZE / 2 + 1);
|
||||
int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - KEYFRAME_SIZE / 2);
|
||||
if (startX < endX) {
|
||||
renderer.drawRect(startX + 1, BORDER_TOP + 1, endX - startX - 2, KEYFRAME_SIZE - 2, 0xFF0088FF);
|
||||
// Draw red quads on time path segments that would require time going backwards
|
||||
for (PathSegment segment : mod.getCurrentTimeline().getPaths().get(GuiPathing.TIME_PATH).getSegments()) {
|
||||
long startTimestamp = segment.getStartKeyframe().getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new);
|
||||
long endTimestamp = segment.getEndKeyframe().getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new);
|
||||
if (endTimestamp >= startTimestamp) {
|
||||
continue; // All is fine, time is not moving backwards
|
||||
}
|
||||
drawQuadOnSegment(renderer, visibleWidth, segment, BORDER_TOP + KEYFRAME_SIZE + 1, 0xFFFF0000);
|
||||
}
|
||||
|
||||
super.drawTimelineCursor(renderer, size);
|
||||
}
|
||||
|
||||
private void drawQuadOnSegment(GuiRenderer renderer, int visibleWidth, PathSegment segment, int y, int color) {
|
||||
int startTime = getOffset();
|
||||
int visibleTime = (int) (getZoom() * getLength());
|
||||
int endTime = getOffset() + visibleTime;
|
||||
|
||||
long startFrameTime = segment.getStartKeyframe().getTime();
|
||||
long endFrameTime = segment.getEndKeyframe().getTime();
|
||||
if (startFrameTime >= endTime || endFrameTime <= startTime) {
|
||||
return; // Segment out of display range
|
||||
}
|
||||
|
||||
double relativeStart = startFrameTime - startTime;
|
||||
double relativeEnd = endFrameTime - startTime;
|
||||
int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + KEYFRAME_SIZE / 2 + 1);
|
||||
int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - KEYFRAME_SIZE / 2);
|
||||
if (startX < endX) {
|
||||
renderer.drawRect(startX + 1, y, endX - startX - 2, KEYFRAME_SIZE - 2, color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keyframe at the specified position.
|
||||
* @param position The raw position
|
||||
@@ -208,6 +226,7 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline
|
||||
// Clicked on timeline but not on any keyframe
|
||||
if (button == 0) { // Left click
|
||||
setCursorPosition(time);
|
||||
gui.getMod().setSelectedKeyframe(null);
|
||||
} else if (button == 1) { // Right click
|
||||
if (pathKeyframePair.getLeft() != null) {
|
||||
// Apply the value of the clicked path at the clicked position
|
||||
@@ -242,6 +261,15 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline
|
||||
@Override
|
||||
public boolean mouseDrag(ReadablePoint position, int button, long timeSinceLastCall) {
|
||||
if (!dragging) {
|
||||
if (button == 0) {
|
||||
// Left click, the user might try to move the cursor by clicking and holding
|
||||
int time = getTimeAt(position.getX(), position.getY());
|
||||
if (time != -1) {
|
||||
// and they are still on the timeline, so update the time appropriately
|
||||
setCursorPosition(time);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
|
||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
|
||||
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
|
||||
import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -69,22 +70,69 @@ public class GuiPathing {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
public final GuiTexturedButton playPauseButton = new GuiTexturedButton().setSize(20, 20)
|
||||
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE);
|
||||
public final GuiTexturedButton playPauseButton = new GuiTexturedButton() {
|
||||
@Override
|
||||
public GuiElement getTooltip(RenderInfo renderInfo) {
|
||||
GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo);
|
||||
if (tooltip != null) {
|
||||
if (player.isActive()) {
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.pausepath");
|
||||
} else if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)) {
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.playpathfromstart");
|
||||
} else {
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.playpath");
|
||||
}
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
}.setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTooltip(new GuiTooltip());
|
||||
|
||||
public final GuiTexturedButton renderButton = new GuiTexturedButton().onClick(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
preparePathsForPlayback();
|
||||
if (!preparePathsForPlayback()) return;
|
||||
new GuiRenderSettings(replayHandler, mod.getCurrentTimeline()).display();
|
||||
}
|
||||
}).setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 0);
|
||||
}).setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 0)
|
||||
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.renderpath"));
|
||||
|
||||
public final GuiTexturedButton positionKeyframeButton = new GuiTexturedButton().setSize(20, 20)
|
||||
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE);
|
||||
public final GuiTexturedButton positionKeyframeButton = new GuiTexturedButton() {
|
||||
@Override
|
||||
public GuiElement getTooltip(RenderInfo renderInfo) {
|
||||
GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo);
|
||||
if (tooltip != null) {
|
||||
if (getTextureNormal().getY() == 40) { // Add keyframe
|
||||
if (getTextureNormal().getX() == 0) { // Position
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.addposkeyframe");
|
||||
} else { // Spectator
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.addspeckeyframe");
|
||||
}
|
||||
} else { // Remove keyframe
|
||||
if (getTextureNormal().getX() == 0) { // Position
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.removeposkeyframe");
|
||||
} else { // Spectator
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.removespeckeyframe");
|
||||
}
|
||||
}
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
}.setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTooltip(new GuiTooltip());
|
||||
|
||||
public final GuiTexturedButton timeKeyframeButton = new GuiTexturedButton().setSize(20, 20)
|
||||
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE);
|
||||
public final GuiTexturedButton timeKeyframeButton = new GuiTexturedButton() {
|
||||
@Override
|
||||
public GuiElement getTooltip(RenderInfo renderInfo) {
|
||||
GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo);
|
||||
if (tooltip != null) {
|
||||
if (getTextureNormal().getY() == 80) { // Add time keyframe
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.addtimekeyframe");
|
||||
} else { // Remove time keyframe
|
||||
tooltip.setI18nText("replaymod.gui.ingame.menu.removetimekeyframe");
|
||||
}
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
}.setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTooltip(new GuiTooltip());
|
||||
|
||||
public final GuiKeyframeTimeline timeline = new GuiKeyframeTimeline(this){
|
||||
@Override
|
||||
@@ -113,14 +161,16 @@ public class GuiPathing {
|
||||
public void run() {
|
||||
zoomTimeline(2d / 3d);
|
||||
}
|
||||
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20);
|
||||
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20)
|
||||
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomin"));
|
||||
|
||||
public final GuiTexturedButton zoomOutButton = new GuiTexturedButton().setSize(9, 9).onClick(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
zoomTimeline(3d / 2d);
|
||||
}
|
||||
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30);
|
||||
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30)
|
||||
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomout"));
|
||||
|
||||
public final GuiPanel zoomButtonPanel = new GuiPanel()
|
||||
.setLayout(new VerticalLayout(VerticalLayout.Alignment.CENTER).setSpacing(2))
|
||||
@@ -231,10 +281,12 @@ public class GuiPathing {
|
||||
Timeline timeline = mod.getCurrentTimeline();
|
||||
Path timePath = timeline.getPaths().get(TIME_PATH);
|
||||
|
||||
preparePathsForPlayback();
|
||||
if (!preparePathsForPlayback()) return;
|
||||
|
||||
timePath.setActive(!Keyboard.isKeyDown(Keyboard.KEY_LSHIFT));
|
||||
ListenableFuture<Void> future = player.start(timeline);
|
||||
// Start from cursor time unless the control key is pressed (then start from beginning)
|
||||
int startTime = Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)? 0 : GuiPathing.this.timeline.getCursorPosition();
|
||||
ListenableFuture<Void> future = player.start(timeline, startTime);
|
||||
overlay.setCloseable(false);
|
||||
overlay.setMouseVisible(true);
|
||||
Futures.addCallback(future, new FutureCallback<Void>() {
|
||||
@@ -403,6 +455,15 @@ public class GuiPathing {
|
||||
});
|
||||
});
|
||||
|
||||
core.getKeyBindingRegistry().registerRaw(Keyboard.KEY_DELETE, () -> {
|
||||
if (!overlay.isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (mod.getSelectedKeyframe() != null) {
|
||||
updateKeyframe(mod.getSelectedKeyframe().getValue(TimestampProperty.PROPERTY).isPresent());
|
||||
}
|
||||
});
|
||||
|
||||
// Start loading entity tracker
|
||||
entityTrackerFuture = SettableFuture.create();
|
||||
new Thread(() -> {
|
||||
@@ -429,10 +490,27 @@ public class GuiPathing {
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void preparePathsForPlayback() {
|
||||
private boolean preparePathsForPlayback() {
|
||||
Timeline timeline = mod.getCurrentTimeline();
|
||||
timeline.getPaths().get(TIME_PATH).updateAll();
|
||||
timeline.getPaths().get(POSITION_PATH).updateAll();
|
||||
|
||||
// Make sure time keyframes's values are monotonically increasing
|
||||
int lastTime = 0;
|
||||
for (Keyframe keyframe : timeline.getPaths().get(TIME_PATH).getKeyframes()) {
|
||||
int time = keyframe.getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new);
|
||||
if (time < lastTime) {
|
||||
// We are going backwards in time
|
||||
GuiInfoPopup.open(replayHandler.getOverlay(),
|
||||
"replaymod.error.negativetime1",
|
||||
"replaymod.error.negativetime2",
|
||||
"replaymod.error.negativetime3");
|
||||
return false;
|
||||
}
|
||||
lastTime = time;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void zoomTimeline(double factor) {
|
||||
|
||||
@@ -485,6 +485,9 @@ replaymod.gui.objects=Custom Objects
|
||||
#Errors
|
||||
replaymod.error.unknownrestriction1=This replay cannot be played with your current version.
|
||||
replaymod.error.unknownrestriction2=It tried to enforce %s which is unknown.
|
||||
replaymod.error.negativetime1=Some of your time keyframes are out of order.
|
||||
replaymod.error.negativetime2=Going backwards in time is not supported.
|
||||
replaymod.error.negativetime3=The invalid parts are marked in red.
|
||||
|
||||
#Replay Mod Incompatibility Warning
|
||||
replaymod.gui.modwarning.title=Incompatibility detected
|
||||
@@ -496,4 +499,14 @@ replaymod.gui.modwarning.id=Mod Id:
|
||||
replaymod.gui.modwarning.missing=Missing Mods
|
||||
replaymod.gui.modwarning.version=Different Mod Versions
|
||||
replaymod.gui.modwarning.version.expected=Expected
|
||||
replaymod.gui.modwarning.version.found=Found
|
||||
replaymod.gui.modwarning.version.found=Found
|
||||
|
||||
#OpenEye
|
||||
replaymod.gui.offeropeneye1=Do you want to install OpenEye?
|
||||
replaymod.gui.offeropeneye2=OpenEye is a mod that collects anonymous information
|
||||
replaymod.gui.offeropeneye3=about the mods being run and any crashes that happen.
|
||||
replaymod.gui.offeropeneye4=
|
||||
replaymod.gui.offeropeneye5=OpenEye can be disabled at any time and is completely optional.
|
||||
replaymod.gui.offeropeneye6=More information is available at https://openeye.openmods.info/faq
|
||||
replaymod.gui.offeropeneye7=Clicking Yes will download OpenEye and place it in your mods folder.
|
||||
replaymod.gui.offeropeneye8=It will then be active the next time you start Minecraft.
|
||||
@@ -116,5 +116,22 @@
|
||||
"parent": "replaymod",
|
||||
"screenshots": [],
|
||||
"dependencies": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"modid": "replaymod-compat",
|
||||
"name": "Replay Mod - Compatibility",
|
||||
"description": "Compatibility Module of the ReplayMod - Adds compatibility with other mods",
|
||||
"version": "${version}",
|
||||
"mcversion": "${mcversion}",
|
||||
"url": "https://replaymod.com",
|
||||
"updateUrl": "https://replaymod.com/download",
|
||||
"authorList": [
|
||||
"CrushedPixel",
|
||||
"johni0702"
|
||||
],
|
||||
"logoFile": "replaymod_logo.png",
|
||||
"parent": "replaymod",
|
||||
"screenshots": [],
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
|
||||
12
src/main/resources/mixins.compat.shaders.replaymod.json
Normal file
12
src/main/resources/mixins.compat.shaders.replaymod.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"required": false,
|
||||
"package": "com.replaymod.compat.shaders.mixin",
|
||||
"mixins": [],
|
||||
"server": [],
|
||||
"client": [
|
||||
"MixinShaderEntityRenderer",
|
||||
"MixinShaderRenderChunk",
|
||||
"MixinShaderRenderGlobal"
|
||||
],
|
||||
"refmap": "mixins.replaymod.refmap.json"
|
||||
}
|
||||
Reference in New Issue
Block a user