Compare commits

..

25 Commits

Author SHA1 Message Date
Jonas Herzig
ed566732df Merge branch '1.8' into 1.8.9
91573d9 Update jGui, ReplayStudio and Translations
d71358b Add confirmation dialog before overwriting path presets (fixes #65)
1a55983 Move addCallback method from integration test Utils to main Utils
18c5bcd Be more cooperative with other mods on the ingame menu (fixes #42)
c054fe8 Register keys for simplepathing only once (fixes #63)
f13297c Fix default interpolator handling in keyframe gui and after loading (fixes #64)
391f304 Show error popup if entity tracker fails to load
0e0eaaa Fix entity tracker being set even if it failed loading (fixes #50)
a34bbbc Fix NPE when spectating a player without a camera entity
60879fb Change local class to be an inner class because Srg2Source breaks with it
dfafbec Load translations from github repo, only reload language not all resource packs
8a6ec51 Add missing tooltips to player overview and various missing chat messages
5677fc5 Re-add warning to replay editor
748a91e Fix missing tooltips on fav/like/dislike buttons in replay center
ee24866 Fix upload replay button not being updated when name/tags change
dba085c Move translations into separate repo
3f0e3e7 Fix NPE when receiving Replay|Restrict messages (fixes #16 GH)
40e1d85 Fix crash when saving the last keyframe in the edit gui (fixes #61)
03aada1 Update Mixin to 0.6.8 (fixes #9 GH)
0c1dc65 Fix interpolator being lost when moving keyframe to the end (fixes #62)
fcbbbc9 Add integration test. Run with ./gradlew runIntegrationTest
fe6ded0 Fix movement of keyframe via GuiEditKeyframe not updating selected keyframe
f58fa8f Fix Login GUI not respecting other GUIs opened on startup
4fc3a31 Fix OpenEye being installed into working dir instead of mcDataDir
2017-05-31 09:36:46 +02:00
Jonas Herzig
91573d9487 Update jGui, ReplayStudio and Translations
jGui:
9e84e72 Fix inversion of empty selection

ReplayStudio:
ff12b1d Remove invalid filter from services files
c6a9325 Proper value wrapping in CatmullRomSplineInterpolator (fixes #8 GH)
2017-05-31 09:16:19 +02:00
Jonas Herzig
d71358bcba Add confirmation dialog before overwriting path presets (fixes #65) 2017-05-21 19:34:46 +02:00
Jonas Herzig
1a55983986 Move addCallback method from integration test Utils to main Utils 2017-05-21 19:34:46 +02:00
Jonas Herzig
18c5bcd469 Be more cooperative with other mods on the ingame menu (fixes #42) 2017-05-21 18:59:29 +02:00
Jonas Herzig
c054fe83f6 Register keys for simplepathing only once (fixes #63) 2017-05-21 17:11:15 +02:00
Jonas Herzig
f13297c5a5 Fix default interpolator handling in keyframe gui and after loading (fixes #64) 2017-05-21 16:29:59 +02:00
Jonas Herzig
391f304c5f Show error popup if entity tracker fails to load 2017-05-21 12:42:13 +02:00
Jonas Herzig
0e0eaaa9a7 Fix entity tracker being set even if it failed loading (fixes #50) 2017-05-21 12:27:20 +02:00
Jonas Herzig
a34bbbcd68 Fix NPE when spectating a player without a camera entity 2017-05-21 11:57:30 +02:00
Jonas Herzig
60879fbbed Change local class to be an inner class because Srg2Source breaks with it 2017-04-21 17:24:43 +02:00
Jonas Herzig
dfafbecf35 Load translations from github repo, only reload language not all resource packs 2017-04-21 16:36:47 +02:00
Jonas Herzig
8a6ec5191b Add missing tooltips to player overview and various missing chat messages 2017-04-21 16:36:47 +02:00
Jonas Herzig
5677fc54cb Re-add warning to replay editor 2017-04-21 16:36:47 +02:00
Jonas Herzig
748a91e7b2 Fix missing tooltips on fav/like/dislike buttons in replay center 2017-04-21 16:36:47 +02:00
Jonas Herzig
ee24866f29 Fix upload replay button not being updated when name/tags change 2017-04-21 16:36:47 +02:00
Jonas Herzig
dba085c700 Move translations into separate repo 2017-04-21 16:36:47 +02:00
Jonas Herzig
3f0e3e725e Fix NPE when receiving Replay|Restrict messages (fixes #16 GH) 2017-04-21 13:38:08 +02:00
johni0702
40e1d850da Fix crash when saving the last keyframe in the edit gui (fixes #61) 2017-04-14 17:31:16 +02:00
johni0702
03aada1f5f Update Mixin to 0.6.8 (fixes #9 GH) 2017-04-14 17:31:16 +02:00
johni0702
0c1dc658ad Fix interpolator being lost when moving keyframe to the end (fixes #62) 2017-04-14 17:31:16 +02:00
johni0702
fcbbbc9ea1 Add integration test. Run with ./gradlew runIntegrationTest 2017-04-14 17:31:15 +02:00
johni0702
fe6ded0143 Fix movement of keyframe via GuiEditKeyframe not updating selected keyframe
Selecting a keyframe, double clicking it to open the gui, changing the
timestamp, saving and then clicking the Add/Remove Keyframe button used to
crash the client. This is fixed by updating the selection whenever the time is
changed.
2017-04-13 18:18:17 +02:00
johni0702
f58fa8f9c2 Fix Login GUI not respecting other GUIs opened on startup
Previously the Login GUI would simply replace the currently active GUI.
Now the currently active GUI is stored and then re-opened after login.
2017-04-13 18:12:07 +02:00
johni0702
4fc3a3166c Fix OpenEye being installed into working dir instead of mcDataDir 2017-04-13 18:06:21 +02:00
104 changed files with 2008 additions and 1394 deletions

View File

@@ -7,7 +7,7 @@ pipeline:
commands: commands:
- curl --fail --create-dirs -o $HOME/.gradle/init.d/mirror.gradle https://maven.johni0702.de/config/gradle - curl --fail --create-dirs -o $HOME/.gradle/init.d/mirror.gradle https://maven.johni0702.de/config/gradle
- curl --fail --create-dirs -o $HOME/.m2/settings.xml https://maven.johni0702.de/config/maven - curl --fail --create-dirs -o $HOME/.m2/settings.xml https://maven.johni0702.de/config/maven
- JAVA_OPTS=-Xmx1516M ./gradlew setupDecompWorkspace - ./gradlew setupCIWorkspace
- ./gradlew :build - ./gradlew :build
archive: archive:
image: plugins/s3 image: plugins/s3

View File

@@ -1 +1 @@
eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgY2xvbmU6CiAgICBpbWFnZTogcGx1Z2lucy9naXQKICAgIHRhZ3M6IHRydWUKICBidWlsZDoKICAgIGltYWdlOiBtYXZlbjozLjMuOS1qZGstOAogICAgY29tbWFuZHM6CiAgICAgIC0gY3VybCAtLWZhaWwgLS1jcmVhdGUtZGlycyAtbyAkSE9NRS8uZ3JhZGxlL2luaXQuZC9taXJyb3IuZ3JhZGxlIGh0dHBzOi8vbWF2ZW4uam9obmkwNzAyLmRlL2NvbmZpZy9ncmFkbGUKICAgICAgLSBjdXJsIC0tZmFpbCAtLWNyZWF0ZS1kaXJzIC1vICRIT01FLy5tMi9zZXR0aW5ncy54bWwgaHR0cHM6Ly9tYXZlbi5qb2huaTA3MDIuZGUvY29uZmlnL21hdmVuCiAgICAgIC0gSkFWQV9PUFRTPS1YbXgxNTE2TSAuL2dyYWRsZXcgc2V0dXBEZWNvbXBXb3Jrc3BhY2UKICAgICAgLSAuL2dyYWRsZXcgOmJ1aWxkCiAgYXJjaGl2ZToKICAgIGltYWdlOiBwbHVnaW5zL3MzCiAgICBhY2w6IHB1YmxpYy1yZWFkCiAgICByZWdpb246ICJ1cy1lYXN0LTEiCiAgICBidWNrZXQ6ICJyZXBsYXltb2QiCiAgICBwYXRoX3N0eWxlOiB0cnVlCiAgICBzb3VyY2U6IGJ1aWxkL2xpYnMvKgogICAgc3RyaXBfcHJlZml4OiBidWlsZC9saWJzLwogICAgdGFyZ2V0OiAvCg.nzPW0_TpMIjnFZZ5Ci439QDKlaDo1a3DCH-VCYUgpx8 eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgY2xvbmU6CiAgICBpbWFnZTogcGx1Z2lucy9naXQKICAgIHRhZ3M6IHRydWUKICBidWlsZDoKICAgIGltYWdlOiBtYXZlbjozLjMuOS1qZGstOAogICAgY29tbWFuZHM6CiAgICAgIC0gY3VybCAtLWZhaWwgLS1jcmVhdGUtZGlycyAtbyAkSE9NRS8uZ3JhZGxlL2luaXQuZC9taXJyb3IuZ3JhZGxlIGh0dHBzOi8vbWF2ZW4uam9obmkwNzAyLmRlL2NvbmZpZy9ncmFkbGUKICAgICAgLSBjdXJsIC0tZmFpbCAtLWNyZWF0ZS1kaXJzIC1vICRIT01FLy5tMi9zZXR0aW5ncy54bWwgaHR0cHM6Ly9tYXZlbi5qb2huaTA3MDIuZGUvY29uZmlnL21hdmVuCiAgICAgIC0gLi9ncmFkbGV3IHNldHVwQ0lXb3Jrc3BhY2UKICAgICAgLSAuL2dyYWRsZXcgOmJ1aWxkCiAgYXJjaGl2ZToKICAgIGltYWdlOiBwbHVnaW5zL3MzCiAgICBhY2w6IHB1YmxpYy1yZWFkCiAgICByZWdpb246ICJ1cy1lYXN0LTEiCiAgICBidWNrZXQ6ICJyZXBsYXltb2QiCiAgICBwYXRoX3N0eWxlOiB0cnVlCiAgICBzb3VyY2U6IGJ1aWxkL2xpYnMvKgogICAgc3RyaXBfcHJlZml4OiBidWlsZC9saWJzLwogICAgdGFyZ2V0OiAvCg.riE6lhwfdNUhI7LO2SxEKFOjzjaX40ne7Ae9gyP75KE

4
.gitmodules vendored
View File

@@ -5,3 +5,7 @@
[submodule "ReplayStudio"] [submodule "ReplayStudio"]
path = ReplayStudio path = ReplayStudio
url = https://github.com/ReplayMod/ReplayStudio url = https://github.com/ReplayMod/ReplayStudio
[submodule "src/main/resources/assets/replaymod/lang"]
path = src/main/resources/assets/replaymod/lang
url = https://github.com/ReplayMod/Translations

View File

@@ -1,7 +1,5 @@
import groovy.json.JsonOutput import groovy.json.JsonOutput
import java.util.zip.ZipInputStream
buildscript { buildscript {
repositories { repositories {
mavenCentral() mavenCentral()
@@ -20,7 +18,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT' classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT'
classpath 'org.spongepowered:mixingradle:0.4-SNAPSHOT' classpath 'org.spongepowered:mixingradle:0.3-SNAPSHOT'
} }
} }
@@ -36,9 +34,9 @@ archivesBaseName = "replaymod"
minecraft { minecraft {
coreMod = 'com.replaymod.core.LoadingPlugin' coreMod = 'com.replaymod.core.LoadingPlugin'
version = '1.10.2-12.18.2.2099' version = '1.8.9-11.15.1.1722'
runDir = "eclipse" runDir = "eclipse"
mappings = "snapshot_20160518" mappings = "stable_22"
replace '@MOD_VERSION@', project.version replace '@MOD_VERSION@', project.version
replace '@MC_VERSION@', project.minecraft.version replace '@MC_VERSION@', project.minecraft.version
} }
@@ -61,7 +59,7 @@ configurations {
dependencies { dependencies {
compile 'org.projectlombok:lombok:1.16.4' compile 'org.projectlombok:lombok:1.16.4'
compile 'org.spongepowered:mixin:0.6.4-SNAPSHOT' compile 'org.spongepowered:mixin:0.6.8-SNAPSHOT'
shade 'com.googlecode.mp4parser:isoparser:1.1.7' shade 'com.googlecode.mp4parser:isoparser:1.1.7'
shade 'org.apache.commons:commons-exec:1.3' shade 'org.apache.commons:commons-exec:1.3'
shade 'com.google.apis:google-api-services-youtube:v3-rev178-1.22.0' shade 'com.google.apis:google-api-services-youtube:v3-rev178-1.22.0'
@@ -103,27 +101,6 @@ jar {
} }
from noticeDir from noticeDir
def langDir = file("$buildDir/languages")
doFirst {
try {
langDir.deleteDir()
langDir.mkdirs()
def dir = new File(langDir, 'assets/replaymod/lang/')
dir.mkdirs()
def zip = new ZipInputStream(new URL('http://replaymod.com/api/grab_languages').openStream())
def e;
while ((e = zip.nextEntry) != null) {
new File(dir, e.getName()) << zip
zip.closeEntry()
}
} catch(Exception e) {
e.printStackTrace();
}
}
from (langDir) {
exclude '**/en_US.lang'
}
from ({shade().collect { it.isDirectory() ? it : zipTree(it) }}) { from ({shade().collect { it.isDirectory() ? it : zipTree(it) }}) {
exclude '**/NOTICE*' exclude '**/NOTICE*'
// exclude everything taken in from jGui for running the mod in a dev environment // exclude everything taken in from jGui for running the mod in a dev environment
@@ -138,7 +115,6 @@ jar {
manifest { manifest {
attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker', attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
'TweakOrder': '0', 'TweakOrder': '0',
'FMLCorePluginContainsFMLMod': 'true',
'FMLAT': 'replaymod_at.cfg' 'FMLAT': 'replaymod_at.cfg'
} }
} }
@@ -189,6 +165,13 @@ sourceSets {
} }
refMap = "mixins.replaymod.refmap.json" refMap = "mixins.replaymod.refmap.json"
} }
integrationTest {
compileClasspath += main.runtimeClasspath + main.output
java {
srcDir file('src/integration-test/java')
}
resources.srcDir file('src/integration-test/resources')
}
} }
task copySrg(type: Copy, dependsOn: 'genSrgs') { task copySrg(type: Copy, dependsOn: 'genSrgs') {
@@ -200,6 +183,30 @@ setupDecompWorkspace.dependsOn copySrg
setupDevWorkspace.dependsOn copySrg setupDevWorkspace.dependsOn copySrg
project.tasks.idea.dependsOn copySrg project.tasks.idea.dependsOn copySrg
task runIntegrationTest(type: JavaExec, dependsOn: ["makeStart", "jar"]) {
main = 'GradleStart'
standardOutput = System.out
errorOutput = System.err
workingDir file(minecraft.runDir)
def testDir = new File(minecraft.runDir, "integration-test")
doFirst {
testDir.deleteDir()
testDir.mkdirs()
}
doLast {
testDir.deleteDir()
}
afterEvaluate {
def runClient = tasks.getByName("runClient")
runIntegrationTest.jvmArgs = runClient.jvmArgs + "-Dfml.noGrab=true"
runIntegrationTest.args = runClient.args + "--gameDir" + testDir.canonicalPath
runIntegrationTest.classpath runClient.classpath + sourceSets.integrationTest.output
}
}
def generateVersionsJson() { def generateVersionsJson() {
// List all tags // List all tags
def stdout = new ByteArrayOutputStream() def stdout = new ByteArrayOutputStream()

2
jGui

Submodule jGui updated: 26af916257...1e92ad85df

View File

@@ -0,0 +1,30 @@
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Entry point for running integration tests from within the IDE.
* This is <b>not</b> called when running integration tests directly from gradle.
*/
public class IntegrationTest {
public static void main(String[] args) throws Throwable {
// Prevent MC from grabbing our mouse during the test (it's not needed)
System.setProperty("fml.noGrab", "true");
// Make sure the test folder exists and is fresh
File gameDir = new File(System.getProperty("user.dir"), "integration-test");
if (gameDir.exists()) {
FileUtils.forceDelete(gameDir);
}
FileUtils.forceMkdir(gameDir);
// Set game dir to test folder and call regular entry point
List<String> argsList = new ArrayList<>(Arrays.asList(args));
argsList.add("--gameDir");
argsList.add(gameDir.getCanonicalPath());
GradleStart.main(argsList.toArray(new String[argsList.size()]));
}
}

View File

@@ -0,0 +1,342 @@
package com.replaymod.core;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import de.johni0702.minecraft.gui.container.AbstractGuiOverlay;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiOverlay;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiElement;
import de.johni0702.minecraft.gui.element.GuiTexturedButton;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.utils.Consumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Keyboard;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeoutException;
import static com.replaymod.core.ReplayModIntegrationTest.LOGGER;
import static com.replaymod.core.utils.Utils.addCallback;
public abstract class AbstractTask implements Task {
public static Task create(Consumer<AbstractTask> init) {
return new AbstractTask() {
@Override
protected void init() {
init.consume(this);
}
};
}
public static final Minecraft mc = Minecraft.getMinecraft();
public final ReplayMod core = ReplayMod.instance;
public SettableFuture<Void> future;
@Override
public ListenableFuture<Void> execute() {
future = SettableFuture.create();
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
addCallback(future, success -> {
FMLCommonHandler.instance().bus().unregister(this);
MinecraftForge.EVENT_BUS.unregister(this);
}, error -> {});
init();
return future;
}
protected void init() {}
protected void runLater(Runnable runnable) {
core.runLater(() -> {
try {
runnable.run();
} catch (Throwable t) {
future.setException(t);
}
});
}
public void expectGuiClosed(Runnable onClosed) {
expectGuiClosed0(10, onClosed);
}
public void expectGuiClosed(int timeout, Runnable onClosed) {
expectGuiClosed0(timeout, onClosed);
}
private void expectGuiClosed0(int timeout, Runnable onClosed) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
class EventHandler {
final net.minecraft.client.gui.GuiScreen currentScreen = mc.currentScreen;
int framesPassed;
@SubscribeEvent
public void onGuiOpen(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (currentScreen != mc.currentScreen) {
FMLCommonHandler.instance().bus().unregister(this);
onClosed.run();
} else {
if (framesPassed < timeout) {
framesPassed++;
} else {
Object gui = (gui = GuiScreen.from(currentScreen)) == null ? currentScreen : gui;
Exception e = new TimeoutException("Timeout while waiting for " + gui + " to be closed.");
e.setStackTrace(Arrays.copyOfRange(stackTrace, 3, stackTrace.length));
future.setException(e);
}
}
}
}
FMLCommonHandler.instance().bus().register(new EventHandler());
}
public void expectPopupClosed(Runnable onClosed) {
expectPopupClosed0(10, onClosed);
}
public void expectPopupClosed(int timeout, Runnable onClosed) {
expectPopupClosed0(timeout, onClosed);
}
private void expectPopupClosed0(int timeout, Runnable onClosed) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
AbstractGuiPopup popup = getPopup(mc.currentScreen);
if (popup == null) {
throw new IllegalStateException("No popup found.");
}
class EventHandler {
int framesPassed;
@SubscribeEvent
public void onGuiOpen(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (getPopup(mc.currentScreen) != popup) {
FMLCommonHandler.instance().bus().unregister(this);
onClosed.run();
} else {
if (framesPassed < timeout) {
framesPassed++;
} else {
Exception e = new TimeoutException("Timeout while waiting for " + popup + " to be closed.");
e.setStackTrace(Arrays.copyOfRange(stackTrace, 3, stackTrace.length));
future.setException(e);
}
}
}
}
FMLCommonHandler.instance().bus().register(new EventHandler());
}
private AbstractGuiPopup getPopup(net.minecraft.client.gui.GuiScreen minecraft) {
GuiContainer<?> container = GuiOverlay.from(minecraft);
if (container == null) {
container = GuiScreen.from(minecraft);
}
if (container != null) {
while (container.getContainer() != null) {
container = container.getContainer();
}
GuiElement popup = Iterables.getLast(container.getChildren());
if (popup instanceof AbstractGuiPopup) {
return (AbstractGuiPopup) popup;
}
}
return null;
}
public <T> void expectGui(Class<T> guiClass, Consumer<T> onOpen) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
class EventHandler {
net.minecraft.client.gui.GuiScreen currentScreen;
int framesPassed;
@SubscribeEvent
public void onGuiOpen(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (currentScreen != mc.currentScreen) {
currentScreen = mc.currentScreen;
framesPassed = 0;
}
if (framesPassed < 10) {
framesPassed++;
return;
}
FMLCommonHandler.instance().bus().unregister(this);
Object foundGui = null;
if (AbstractGuiScreen.class.isAssignableFrom(guiClass)) {
AbstractGuiScreen guiScreen = GuiScreen.from(currentScreen);
if (guiClass.isInstance(guiScreen)) {
onOpen.consume(guiClass.cast(guiScreen));
return;
}
foundGui = guiScreen;
} else if (AbstractGuiOverlay.class.isAssignableFrom(guiClass)) {
AbstractGuiOverlay guiScreen = GuiOverlay.from(currentScreen);
if (guiClass.isInstance(guiScreen)) {
onOpen.consume(guiClass.cast(guiScreen));
return;
}
foundGui = guiScreen;
} else if (AbstractGuiPopup.class.isAssignableFrom(guiClass)) {
AbstractGuiPopup popup = getPopup(currentScreen);
if (guiClass.isInstance(popup)) {
onOpen.consume(guiClass.cast(popup));
return;
}
} else {
if (guiClass.isInstance(currentScreen)) {
onOpen.consume(guiClass.cast(currentScreen));
return;
}
}
class UnexpectedGuiException extends Exception {
UnexpectedGuiException(Object foundGui) {
super("Expected instance of " + guiClass + " but found " + foundGui);
setStackTrace(Arrays.copyOfRange(stackTrace, 2, stackTrace.length));
}
}
future.setException(new UnexpectedGuiException(foundGui == null ? currentScreen : foundGui));
}
}
FMLCommonHandler.instance().bus().register(new EventHandler());
}
private void clickNow(int x, int y) {
try {
Method method = net.minecraft.client.gui.GuiScreen.class
.getDeclaredMethod("mouseClicked", int.class, int.class, int.class);
method.setAccessible(true);
method.invoke(mc.currentScreen, x, y, 0);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
future.setException(e);
}
}
public void click(int x, int y) {
runLater(() -> {
LOGGER.info("Clicking at {}/{}", x, y);
clickNow(x, y);
});
}
private void dragNow(int x, int y) {
try {
Method method = net.minecraft.client.gui.GuiScreen.class
.getDeclaredMethod("mouseClickMove", int.class, int.class, int.class, long.class);
method.setAccessible(true);
method.invoke(mc.currentScreen, x, y, 0, 0);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
future.setException(e);
}
}
public void drag(int x, int y) {
runLater(() -> {
LOGGER.info("Dragging to {}/{}", x, y);
dragNow(x, y);
});
}
public void click(GuiButton button) {
runLater(() -> {
if (!button.isEnabled()) {
future.setException(new IllegalStateException("Button is disabled: " + button.getLabel()));
return;
}
LOGGER.info("Clicking button {}", button.getLabel());
button.onClick();
});
}
public void click(GuiTexturedButton button) {
runLater(() -> {
if (!button.isEnabled()) {
future.setException(new IllegalStateException("Button is disabled: " + button.getTexture()));
return;
}
LOGGER.info("Clicking textured button {}", button.getTexture());
button.onClick();
});
}
public void click(String buttonText) {
runLater(() -> {
LOGGER.info("Clicking button {}", buttonText);
try {
Field field = net.minecraft.client.gui.GuiScreen.class.getDeclaredField("buttonList");
field.setAccessible(true);
@SuppressWarnings("unchecked")
List<net.minecraft.client.gui.GuiButton> buttonList = (List)
field.get(mc.currentScreen);
net.minecraft.client.gui.GuiButton button = null;
for (net.minecraft.client.gui.GuiButton guiButton : buttonList) {
if (guiButton.displayString.equals(buttonText)) {
button = guiButton;
}
}
if (button == null) {
future.setException(new NoSuchElementException("No button with label: " + buttonText));
return;
}
clickNow(button.xPosition + 5, button.yPosition + 5);
} catch (IllegalAccessException | NoSuchFieldException e) {
future.setException(e);
}
});
}
public void type(String string) {
for (char c : string.toCharArray()) {
press(c, Keyboard.getKeyIndex(String.valueOf(c).toUpperCase()));
}
}
public void press(int keyCode) {
String keyName = Keyboard.getKeyName(keyCode);
char character = keyName.length() == 1 ? keyName.charAt(0) : '\0';
press(character, keyCode);
}
public void press(char character, int keyCode) {
runLater(() -> {
LOGGER.info("Pressing key {}", Keyboard.getKeyName(keyCode));
if (mc.currentScreen == null || mc.currentScreen.allowUserInput) {
KeyBinding.onTick(keyCode);
FMLCommonHandler.instance().fireKeyInput();
}
if (mc.currentScreen != null) {
try {
Method method = net.minecraft.client.gui.GuiScreen.class
.getDeclaredMethod("keyTyped", char.class, int.class);
method.setAccessible(true);
method.invoke(mc.currentScreen, character, keyCode);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
future.setException(e);
}
}
});
}
}

View File

@@ -0,0 +1,41 @@
package com.replaymod.core;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import static com.replaymod.core.ReplayModIntegrationTest.LOGGER;
import static com.replaymod.core.utils.Utils.addCallback;
public class CompositeTask implements Task {
private SettableFuture<Void> future;
protected final Task[] children;
public CompositeTask(Task[] children) {
this.children = children;
}
@Override
public ListenableFuture<Void> execute() {
future = SettableFuture.create();
executeChild(0);
return future;
}
private void executeChild(int childIndex) {
if (future.isDone()) return;
if (childIndex < children.length) {
ReplayMod.instance.runLater(() -> {
try {
Task task = children[childIndex];
LOGGER.info("Running task {}", task);
ListenableFuture<Void> childFuture = task.execute();
addCallback(childFuture, done -> executeChild(childIndex + 1), err -> future.setException(err));
} catch (Throwable t) {
future.setException(t);
}
});
} else {
future.set(null);
}
}
}

View File

@@ -0,0 +1,73 @@
package com.replaymod.core;
import com.replaymod.core.regression.RegressionTest60;
import com.replaymod.core.regression.RegressionTest61;
import com.replaymod.core.regression.RegressionTest62;
import com.replaymod.extra.DownloadOpenEye;
import com.replaymod.online.SkipLogin;
import com.replaymod.recording.CreateSPWorld;
import com.replaymod.recording.ExitSPWorld;
import com.replaymod.replay.ExitReplay;
import com.replaymod.replay.LoadReplay;
import com.replaymod.replay.OpenReplayViewer;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger;
import static com.replaymod.core.AbstractTask.mc;
import static com.replaymod.core.ReplayModIntegrationTest.MOD_ID;
import static com.replaymod.core.utils.Utils.addCallback;
/**
* Helper mod that initiates the integration tests.
*/
@Mod(modid = MOD_ID)
public class ReplayModIntegrationTest {
public static final String MOD_ID = "replaymod-integration-test";
public static Logger LOGGER;
@Mod.EventHandler
public void init(FMLPreInitializationEvent event) {
LOGGER = event.getModLog();
// Make sure the game window doesn't have to remain in focus during the test
mc.gameSettings.pauseOnLostFocus = false;
runTasks(
new SkipLogin(),
new DownloadOpenEye(),
new CreateSPWorld(),
new Wait(5000),
new ExitSPWorld(),
new OpenReplayViewer(),
new LoadReplay(),
new RegressionTest60(),
new RegressionTest61(),
new RegressionTest62(),
// new AbstractTask() {}, // Uncomment to not exit on success (useful for writing more tests)
new ExitReplay()
);
}
private void runTasks(Task... tests) {
addCallback(new CompositeTask(tests).execute(), success -> {
if (!Minecraft.getMinecraft().hasCrashed) {
LOGGER.info("===================================================");
LOGGER.info("= ALL TESTS PASSED =");
LOGGER.info("===================================================");
FMLCommonHandler.instance().exitJava(0, false);
}
}, error -> {
LOGGER.error("Failed task:", error);
LOGGER.error("===================================================");
LOGGER.error("= TEST FAILED =");
LOGGER.error("===================================================");
FMLCommonHandler.instance().exitJava(1, false);
});
}
}

View File

@@ -0,0 +1,35 @@
package com.replaymod.core;
import com.google.common.util.concurrent.ListenableFuture;
public interface Task {
ListenableFuture<Void> execute();
static Task click(int x, int y) {
return AbstractTask.create(task -> {
task.click(x, y);
task.runLater(() -> task.future.set(null));
});
}
static Task drag(int x, int y) {
return AbstractTask.create(task -> {
task.drag(x, y);
task.runLater(() -> task.future.set(null));
});
}
static Task pressKey(int keyCode) {
return AbstractTask.create(task -> {
task.press(keyCode);
task.runLater(() -> task.future.set(null));
});
}
static Task pressKey(char character, int keyCode) {
return AbstractTask.create(task -> {
task.press(character, keyCode);
task.runLater(() -> task.future.set(null));
});
}
}

View File

@@ -0,0 +1,9 @@
package com.replaymod.core;
public class Utils {
public static void times(int x, Runnable runnable) {
for (int i = 0; i < x; i++) {
runnable.run();
}
}
}

View File

@@ -0,0 +1,21 @@
package com.replaymod.core;
public class Wait extends AbstractTask {
private final int duration;
public Wait(int duration) {
this.duration = duration;
}
@Override
protected void init() {
new Thread(() -> {
try {
Thread.sleep(duration);
runLater(() -> future.set(null));
} catch (InterruptedException e) {
runLater(() -> future.setException(e));
}
}).start();
}
}

View File

@@ -0,0 +1,59 @@
package com.replaymod.core.regression;
import com.replaymod.core.AbstractTask;
import com.replaymod.core.CompositeTask;
import com.replaymod.core.Task;
import com.replaymod.replay.SpectatePlayer;
import com.replaymod.replay.overlay.OverlayGui;
import com.replaymod.simplepathing.GuiPathingTasks;
import com.replaymod.simplepathing.gui.GuiEditKeyframe;
import org.lwjgl.input.Keyboard;
import static com.replaymod.core.AbstractTask.mc;
import static com.replaymod.core.Utils.times;
/**
* Regression test: #60 Crash in path preview when two spectator keyframes are closer than 50ms
*/
public class RegressionTest60 extends CompositeTask {
public RegressionTest60() {
super(new Task[]{
new SpectatePlayer(),
OverlayGui.whileOpened(
// Place first spectator keyframe
new GuiPathingTasks.ClickPositionKeyframeButton(),
// Place second spectator keyframe
Task.click(130, 50),
new GuiPathingTasks.ClickPositionKeyframeButton(),
// Move second keyframe to 20ms on the keyframe timeline
AbstractTask.create(task -> {
// Double click keyframe
task.click(130, 50);
task.click(130, 50);
task.expectGui(GuiEditKeyframe.Spectator.class, gui -> {
// Set ms field to 20
task.click(mc.currentScreen.width / 2 + 80, mc.currentScreen.height / 2);
times(4, () -> task.press(Keyboard.KEY_BACK));
task.type("20");
// Set other fields to 0
times(2, () -> {
task.press(Keyboard.KEY_TAB);
times(4, () -> task.press(Keyboard.KEY_BACK));
task.press(Keyboard.KEY_0);
});
task.click(gui.saveButton);
task.expectPopupClosed(() -> task.future.set(null));
});
}),
// Place second spectator keyframe
new GuiPathingTasks.ClickPositionKeyframeButton()
),
// Stop spectating player
new SpectatePlayer.End(),
// Enable path preview
Task.pressKey(Keyboard.KEY_H),
new GuiPathingTasks.ClearKeyframeTimeline(),
Task.pressKey(Keyboard.KEY_H),
});
}
}

View File

@@ -0,0 +1,35 @@
package com.replaymod.core.regression;
import com.replaymod.core.AbstractTask;
import com.replaymod.core.CompositeTask;
import com.replaymod.core.Task;
import com.replaymod.replay.overlay.OverlayGui;
import com.replaymod.simplepathing.GuiPathingTasks;
import com.replaymod.simplepathing.gui.GuiEditKeyframe;
/**
* Regression test: #61 NPE when saving in edit keyframe gui of last keyframe
*/
public class RegressionTest61 extends CompositeTask {
public RegressionTest61() {
super(new Task[]{
OverlayGui.whileOpened(
// Place keyframe
Task.click(130, 50),
new GuiPathingTasks.ClickPositionKeyframeButton(),
AbstractTask.create(task -> {
// Double click keyframe
task.click(130, 50);
task.click(130, 50);
task.expectGui(GuiEditKeyframe.Position.class, gui -> {
task.click(gui.saveButton);
task.expectPopupClosed(() -> task.future.set(null));
});
}),
// Place second spectator keyframe
new GuiPathingTasks.ClickPositionKeyframeButton()
),
new GuiPathingTasks.ClearKeyframeTimeline(),
});
}
}

View File

@@ -0,0 +1,28 @@
package com.replaymod.core.regression;
import com.replaymod.core.CompositeTask;
import com.replaymod.core.Task;
import com.replaymod.replay.overlay.OverlayGui;
import com.replaymod.simplepathing.GuiPathingTasks;
/**
* Regression test: #62 Swapping the only two existing keyframes causes NPE
*/
public class RegressionTest62 extends CompositeTask {
public RegressionTest62() {
super(new Task[]{
OverlayGui.whileOpened(
// Place first keyframe
Task.click(130, 50),
new GuiPathingTasks.ClickPositionKeyframeButton(),
// Place second keyframe
Task.click(150, 50),
new GuiPathingTasks.ClickPositionKeyframeButton(),
// Move first keyframe past second keyframe
Task.click(130,50),
Task.drag(170,50)
),
new GuiPathingTasks.ClearKeyframeTimeline()
});
}
}

View File

@@ -0,0 +1,25 @@
package com.replaymod.extra;
import com.replaymod.core.AbstractTask;
import com.replaymod.extras.OpenEyeExtra;
import net.minecraftforge.fml.common.Loader;
import java.io.File;
import java.nio.file.NoSuchFileException;
public class DownloadOpenEye extends AbstractTask {
@Override
protected void init() {
expectGui(OpenEyeExtra.OfferGui.class, offerGui -> {
click(offerGui.yesButton);
expectGuiClosed(20 * 1000, () -> {
File targetFile = new File(mc.mcDataDir, "mods/" + Loader.MC_VERSION + "/OpenEye.jar");
if (!targetFile.exists()) {
future.setException(new NoSuchFileException(targetFile.getAbsolutePath()));
} else {
future.set(null);
}
});
});
}
}

View File

@@ -0,0 +1,14 @@
package com.replaymod.online;
import com.replaymod.core.AbstractTask;
import com.replaymod.online.gui.GuiLoginPrompt;
public class SkipLogin extends AbstractTask {
@Override
protected void init() {
expectGui(GuiLoginPrompt.class, gui -> {
click(gui.cancelButton);
expectGuiClosed(() -> future.set(null));
});
}
}

View File

@@ -0,0 +1,32 @@
package com.replaymod.recording;
import com.replaymod.core.AbstractTask;
import net.minecraft.client.gui.GuiCreateWorld;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiSelectWorld;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
public class CreateSPWorld extends AbstractTask {
@Override
protected void init() {
expectGui(GuiMainMenu.class, mainMenu -> {
click("Singleplayer");
expectGui(GuiSelectWorld.class, selectWorld -> {
click("Create New World");
expectGui(GuiCreateWorld.class, createWorld -> {
click("Create New World");
class EventHandler {
@SubscribeEvent
public void onRenderIngame(RenderGameOverlayEvent.Pre event) {
MinecraftForge.EVENT_BUS.unregister(this);
runLater(() -> future.set(null));
}
}
MinecraftForge.EVENT_BUS.register(new EventHandler());
});
});
});
}
}

View File

@@ -0,0 +1,36 @@
package com.replaymod.recording;
import com.replaymod.core.AbstractTask;
import net.minecraft.client.gui.GuiIngameMenu;
import net.minecraft.client.gui.GuiMainMenu;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import java.io.IOException;
public class ExitSPWorld extends AbstractTask {
@Override
protected void init() {
mc.displayInGameMenu();
expectGui(GuiIngameMenu.class, ingameMenu -> {
click("Save and Quit to Title");
expectGui(GuiMainMenu.class, mainMenu -> new Thread(() -> {
try {
while (true) {
String[] dirs = core.getReplayFolder().list(DirectoryFileFilter.DIRECTORY);
if (dirs == null) {
future.setException(new NullPointerException("dirs is null"));
return;
}
if (dirs.length == 0) {
runLater(() -> future.set(null));
return;
}
Thread.sleep(10);
}
} catch (IOException | InterruptedException e) {
future.setException(e);
}
}).start());
});
}
}

View File

@@ -0,0 +1,16 @@
package com.replaymod.replay;
import com.replaymod.core.AbstractTask;
import net.minecraft.client.gui.GuiIngameMenu;
import net.minecraft.client.gui.GuiMainMenu;
public class ExitReplay extends AbstractTask {
@Override
protected void init() {
mc.displayInGameMenu();
expectGui(GuiIngameMenu.class, ingameMenu -> {
click("Exit Replay");
expectGui(GuiMainMenu.class, mainMenu -> future.set(null));
});
}
}

View File

@@ -0,0 +1,32 @@
package com.replaymod.replay;
import com.replaymod.core.AbstractTask;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import de.johni0702.minecraft.gui.function.Clickable;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.util.Point;
import org.lwjgl.util.ReadableDimension;
public class LoadReplay extends AbstractTask {
@Override
protected void init() {
expectGui(GuiReplayViewer.class, replayViewer -> runLater(() -> {
ReadableDimension size = replayViewer.getMaxSize();
// Select first entry
replayViewer.forEach(Clickable.class).mouseClick(new Point(size.getWidth() / 2, 40), 0);
// Load first replay
click(replayViewer.loadButton);
class EventHandler {
@SubscribeEvent
public void onRenderIngame(RenderGameOverlayEvent.Pre event) {
MinecraftForge.EVENT_BUS.unregister(this);
runLater(() -> future.set(null));
}
}
MinecraftForge.EVENT_BUS.register(new EventHandler());
}));
}
}

View File

@@ -0,0 +1,15 @@
package com.replaymod.replay;
import com.replaymod.core.AbstractTask;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import net.minecraft.client.gui.GuiMainMenu;
public class OpenReplayViewer extends AbstractTask {
@Override
protected void init() {
expectGui(GuiMainMenu.class, mainMenu -> {
click("Replay Viewer");
expectGui(GuiReplayViewer.class, replayViewer -> future.set(null));
});
}
}

View File

@@ -0,0 +1,42 @@
package com.replaymod.replay;
import com.replaymod.core.AbstractTask;
import com.replaymod.extras.playeroverview.PlayerOverviewGui;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Keyboard;
import java.util.concurrent.TimeoutException;
public class SpectatePlayer extends AbstractTask {
@Override
protected void init() {
press(Keyboard.KEY_B);
expectGui(PlayerOverviewGui.class, overview -> {
click(overview.getMaxSize().getWidth() / 2 - 60, 60);
press(Keyboard.KEY_ESCAPE);
expectGuiClosed(() -> future.set(null));
});
}
public static class End extends AbstractTask {
private int timeout;
@Override
protected void init() {
runLater(() -> press(Keyboard.KEY_LSHIFT));
}
@SubscribeEvent
public void onTick(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (timeout++ > 20) {
future.setException(new TimeoutException("Camera hasn't stopped spectating."));
return;
}
if (mc.getRenderViewEntity() == mc.thePlayer) {
future.set(null);
}
}
}
}

View File

@@ -0,0 +1,33 @@
package com.replaymod.replay.overlay;
import com.replaymod.core.AbstractTask;
import com.replaymod.core.CompositeTask;
import com.replaymod.core.Task;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import org.lwjgl.input.Keyboard;
public class OverlayGui {
public static Task whileOpened(Task...tasks) {
Task[] nTasks = new Task[tasks.length + 2];
System.arraycopy(tasks, 0, nTasks, 1, tasks.length);
nTasks[0] = new Open();
nTasks[tasks.length + 1] = new Close();
return new CompositeTask(nTasks);
}
public static class Open extends AbstractTask {
@Override
protected void init() {
press(Keyboard.KEY_T);
expectGui(GuiReplayOverlay.class, done -> future.set(null));
}
}
public static class Close extends AbstractTask {
@Override
protected void init() {
press(Keyboard.KEY_ESCAPE);
expectGuiClosed(() -> future.set(null));
}
}
}

View File

@@ -0,0 +1,35 @@
package com.replaymod.simplepathing;
import com.replaymod.core.AbstractTask;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import com.replaymod.simplepathing.gui.GuiPathing;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import org.lwjgl.input.Keyboard;
public abstract class GuiPathingTasks extends AbstractTask {
@Override
protected void init() {
expectGui(GuiReplayOverlay.class, ign -> init0(ReplayModSimplePathing.instance.getGuiPathing()));
}
protected abstract void init0(GuiPathing guiPathing);
public static class ClickPositionKeyframeButton extends GuiPathingTasks {
@Override
protected void init0(GuiPathing guiPathing) {
click(guiPathing.positionKeyframeButton);
runLater(() -> future.set(null));
}
}
public static class ClearKeyframeTimeline extends AbstractTask {
@Override
protected void init() {
press(Keyboard.KEY_C);
expectGui(GuiYesNoPopup.class, popup -> {
click(popup.getYesButton());
expectGuiClosed(() -> future.set(null));
});
}
}
}

View File

@@ -2,7 +2,7 @@ package com.replaymod.compat;
import com.replaymod.compat.optifine.DisableFastRender; import com.replaymod.compat.optifine.DisableFastRender;
import com.replaymod.compat.shaders.ShaderBeginRender; import com.replaymod.compat.shaders.ShaderBeginRender;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus; import net.minecraftforge.fml.common.eventhandler.EventBus;
@@ -16,7 +16,7 @@ public class ReplayModCompat {
@Mod.EventHandler @Mod.EventHandler
public void init(FMLInitializationEvent event) { public void init(FMLInitializationEvent event) {
EventBus bus = MinecraftForge.EVENT_BUS; EventBus bus = FMLCommonHandler.instance().bus();
bus.register(new ShaderBeginRender()); bus.register(new ShaderBeginRender());
bus.register(new DisableFastRender()); bus.register(new DisableFastRender());
} }

View File

@@ -16,6 +16,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
public class KeyBindingRegistry { public class KeyBindingRegistry {
private Map<String, KeyBinding> keyBindings = new HashMap<String, KeyBinding>(); private Map<String, KeyBinding> keyBindings = new HashMap<String, KeyBinding>();
@@ -85,7 +86,7 @@ public class KeyBindingRegistry {
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding"); CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding");
CrashReportCategory category = crashReport.makeCategory("Key Binding"); CrashReportCategory category = crashReport.makeCategory("Key Binding");
category.addCrashSection("Key Binding", keyBinding); category.addCrashSection("Key Binding", keyBinding);
category.setDetail("Handler", runnable::toString); category.addCrashSectionCallable("Handler", runnable::toString);
throw new ReportedException(crashReport); throw new ReportedException(crashReport);
} }
} }
@@ -100,7 +101,12 @@ public class KeyBindingRegistry {
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Raw Key Binding"); CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Raw Key Binding");
CrashReportCategory category = crashReport.makeCategory("Key Binding"); CrashReportCategory category = crashReport.makeCategory("Key Binding");
category.addCrashSection("Key Code", keyCode); category.addCrashSection("Key Code", keyCode);
category.setDetail("Handler", runnable::toString); category.addCrashSectionCallable("Handler", new Callable() {
@Override
public Object call() throws Exception {
return runnable;
}
});
throw new ReportedException(crashReport); throw new ReportedException(crashReport);
} }
} }

View File

@@ -6,6 +6,7 @@ import com.replaymod.core.gui.GuiReplaySettings;
import com.replaymod.core.gui.RestoreReplayGui; import com.replaymod.core.gui.RestoreReplayGui;
import com.replaymod.core.handler.MainMenuHandler; import com.replaymod.core.handler.MainMenuHandler;
import com.replaymod.core.utils.OpenGLUtils; import com.replaymod.core.utils.OpenGLUtils;
import com.replaymod.render.utils.SoundHandler;
import com.replaymod.replaystudio.util.I18n; import com.replaymod.replaystudio.util.I18n;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import lombok.Getter; import lombok.Getter;
@@ -13,11 +14,15 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.GameSettings; import net.minecraft.client.settings.GameSettings;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory; import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.ChatStyle;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.*;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.Mod.EventHandler;
@@ -67,6 +72,8 @@ public class ReplayMod {
@Deprecated @Deprecated
public static Configuration config; public static Configuration config;
@Deprecated
public static SoundHandler soundHandler = new SoundHandler();
private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry(); private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry();
private final SettingsRegistry settingsRegistry = new SettingsRegistry(); private final SettingsRegistry settingsRegistry = new SettingsRegistry();
@@ -109,7 +116,7 @@ public class ReplayMod {
new MainMenuHandler().register(); new MainMenuHandler().register();
MinecraftForge.EVENT_BUS.register(keyBindingRegistry); FMLCommonHandler.instance().bus().register(keyBindingRegistry);
getKeyBindingRegistry().registerKeyBinding("replaymod.input.settings", 0, () -> { getKeyBindingRegistry().registerKeyBinding("replaymod.input.settings", 0, () -> {
new GuiReplaySettings(null, settingsRegistry).display(); new GuiReplaySettings(null, settingsRegistry).display();
@@ -190,13 +197,13 @@ public class ReplayMod {
private void printToChat(boolean warning, String message, Object... args) { private void printToChat(boolean warning, String message, Object... args) {
if (getSettingsRegistry().get(Setting.NOTIFICATIONS)) { if (getSettingsRegistry().get(Setting.NOTIFICATIONS)) {
// Some nostalgia: "§8[§6Replay Mod§8]§r Your message goes here" // Some nostalgia: "§8[§6Replay Mod§8]§r Your message goes here"
Style coloredDarkGray = new Style().setColor(TextFormatting.DARK_GRAY); ChatStyle coloredDarkGray = new ChatStyle().setColor(EnumChatFormatting.DARK_GRAY);
Style coloredGold = new Style().setColor(TextFormatting.GOLD); ChatStyle coloredGold = new ChatStyle().setColor(EnumChatFormatting.GOLD);
ITextComponent text = new TextComponentString("[").setStyle(coloredDarkGray) IChatComponent text = new ChatComponentText("[").setChatStyle(coloredDarkGray)
.appendSibling(new TextComponentTranslation("replaymod.title").setStyle(coloredGold)) .appendSibling(new ChatComponentTranslation("replaymod.title").setChatStyle(coloredGold))
.appendSibling(new TextComponentString("] ")) .appendSibling(new ChatComponentText("] "))
.appendSibling(new TextComponentTranslation(message, args).setStyle(new Style() .appendSibling(new ChatComponentTranslation(message, args).setChatStyle(new ChatStyle()
.setColor(warning ? TextFormatting.RED : TextFormatting.DARK_GREEN))); .setColor(warning ? EnumChatFormatting.RED : EnumChatFormatting.DARK_GREEN)));
// Send message to chat GUI // Send message to chat GUI
// The ingame GUI is initialized at startup, therefore this is possible before the client is connected // The ingame GUI is initialized at startup, therefore this is possible before the client is connected
mc.ingameGUI.getChatGUI().printChatMessage(text); mc.ingameGUI.getChatGUI().printChatMessage(text);

View File

@@ -2,8 +2,8 @@ package com.replaymod.core;
import com.replaymod.core.events.SettingsChangedEvent; import com.replaymod.core.events.SettingsChangedEvent;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.FMLCommonHandler;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
@@ -83,7 +83,7 @@ public class SettingsRegistry {
throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported."); throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
} }
settings.put(key, value); settings.put(key, value);
MinecraftForge.EVENT_BUS.post(new SettingsChangedEvent(this, key)); FMLCommonHandler.instance().bus().post(new SettingsChangedEvent(this, key));
} }
public void save() { public void save() {

View File

@@ -6,6 +6,8 @@ import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.List;
/** /**
* Moves certain buttons on the main menu upwards so we can inject our own. * Moves certain buttons on the main menu upwards so we can inject our own.
*/ */
@@ -16,12 +18,14 @@ public class MainMenuHandler {
@SubscribeEvent @SubscribeEvent
public void onInit(GuiScreenEvent.InitGuiEvent.Post event) { public void onInit(GuiScreenEvent.InitGuiEvent.Post event) {
if (event.getGui() instanceof GuiMainMenu) { if (event.gui instanceof GuiMainMenu) {
for (GuiButton button : event.getButtonList()) { @SuppressWarnings("unchecked")
List<GuiButton> buttonList = event.buttonList;
for (GuiButton button : buttonList) {
// Buttons that aren't in a rectangle directly above our space don't need moving // Buttons that aren't in a rectangle directly above our space don't need moving
if (button.xPosition + button.width < event.getGui().width / 2 - 100 if (button.xPosition + button.width < event.gui.width / 2 - 100
|| button.xPosition > event.getGui().width / 2 + 100 || button.xPosition > event.gui.width / 2 + 100
|| button.yPosition > event.getGui().height / 4 + 10 + 4 * 24) continue; || button.yPosition > event.gui.height / 4 + 10 + 4 * 24) continue;
// Move button up to make space for two rows of buttons // Move button up to make space for two rows of buttons
// and then move back down by 10 to compensate for the space to the exit button that was already there // and then move back down by 10 to compensate for the space to the exit button that was already there
button.yPosition -= 2 * 24 - 10; button.yPosition -= 2 * 24 - 10;

View File

@@ -2,7 +2,7 @@ package com.replaymod.core.utils;
import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
@@ -35,7 +35,7 @@ public class OpenGLUtils {
float f5 = 1.0F / textureHeight; float f5 = 1.0F / textureHeight;
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
VertexBuffer vertexBuffer = tessellator.getBuffer(); WorldRenderer vertexBuffer = tessellator.getWorldRenderer();
GlStateManager.translate(x+(width/2), y+(width/2), 0); GlStateManager.translate(x+(width/2), y+(width/2), 0);
GlStateManager.rotate(rotation, 0, 0, 1); GlStateManager.rotate(rotation, 0, 0, 1);
vertexBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); vertexBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);

View File

@@ -1,7 +1,7 @@
package com.replaymod.core.utils; package com.replaymod.core.utils;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.SPacketCustomPayload; import net.minecraft.network.play.server.S3FPacketCustomPayload;
/** /**
* Restrictions set by the server, * Restrictions set by the server,
@@ -14,7 +14,7 @@ public class Restrictions {
private boolean onlyFirstPerson; private boolean onlyFirstPerson;
private boolean onlyRecordingPlayer; private boolean onlyRecordingPlayer;
public String handle(SPacketCustomPayload packet) { public String handle(S3FPacketCustomPayload packet) {
PacketBuffer buffer = packet.getBufferData(); PacketBuffer buffer = packet.getBufferData();
while (buffer.isReadable()) { while (buffer.isReadable()) {
String name = buffer.readStringFromBuffer(64); String name = buffer.readStringFromBuffer(64);

View File

@@ -2,6 +2,7 @@ package com.replaymod.core.utils;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.AbstractGuiScrollable; import de.johni0702.minecraft.gui.container.AbstractGuiScrollable;
@@ -25,6 +26,7 @@ import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
@@ -45,6 +47,7 @@ import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer;
import static net.minecraft.client.Minecraft.getMinecraft; import static net.minecraft.client.Minecraft.getMinecraft;
@@ -135,7 +138,7 @@ public class Utils {
} }
public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) { public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) {
NetworkPlayerInfo info = getMinecraft().getConnection().getPlayerInfo(uuid); NetworkPlayerInfo info = getMinecraft().getNetHandler().getPlayerInfo(uuid);
ResourceLocation skinLocation; ResourceLocation skinLocation;
if (info != null && info.hasLocationSkin()) { if (info != null && info.hasLocationSkin()) {
skinLocation = info.getLocationSkin(); skinLocation = info.getLocationSkin();
@@ -149,6 +152,20 @@ public class Utils {
return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL); return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL);
} }
public static <T> void addCallback(ListenableFuture<T> future, Consumer<T> onSuccess, Consumer<Throwable> onFailure) {
Futures.addCallback(future, new FutureCallback<T>() {
@Override
public void onSuccess(@Nullable T result) {
onSuccess.accept(result);
}
@Override
public void onFailure(@Nonnull Throwable t) {
onFailure.accept(t);
}
});
}
public static GuiInfoPopup error(Logger logger, GuiContainer container, CrashReport crashReport, Runnable onClose) { public static GuiInfoPopup error(Logger logger, GuiContainer container, CrashReport crashReport, Runnable onClose) {
// Convert crash report to string // Convert crash report to string
String crashReportStr = crashReport.getCompleteReport(); String crashReportStr = crashReport.getCompleteReport();

View File

@@ -59,6 +59,9 @@ public class GuiReplayEditor extends GuiScreen {
public GuiButton currentTabButton; public GuiButton currentTabButton;
public GuiPanel currentTabPanel; public GuiPanel currentTabPanel;
public final GuiLabel warningLabel = new GuiLabel(this).setColor(Colors.RED)
.setI18nText("replaymod.gui.editor.disclaimer");
public final GuiPanel tabButtons = new GuiPanel(this).setLayout(new GridLayout().setSpacingX(5)); public final GuiPanel tabButtons = new GuiPanel(this).setLayout(new GridLayout().setSpacingX(5));
public final List<GuiPanel> tabPanels = new ArrayList<>(); public final List<GuiPanel> tabPanels = new ArrayList<>();
@@ -84,15 +87,18 @@ public class GuiReplayEditor extends GuiScreen {
// Move all inactive panels aside // Move all inactive panels aside
tabPanels.forEach(e -> pos(e, Integer.MIN_VALUE, Integer.MIN_VALUE)); tabPanels.forEach(e -> pos(e, Integer.MIN_VALUE, Integer.MIN_VALUE));
pos(warningLabel, 10, 22);
size(warningLabel, width - 20, 10);
pos(tabButtons, 10, y(warningLabel) + height(warningLabel) + 2);
size(tabButtons, width - 20, 20);
pos(buttonPanel, width - 10 - width(buttonPanel), height - 10 - height(buttonPanel)); pos(buttonPanel, width - 10 - width(buttonPanel), height - 10 - height(buttonPanel));
if (currentTabPanel != null) { if (currentTabPanel != null) {
pos(currentTabPanel, 10, 50); pos(currentTabPanel, 10, y(tabButtons) + height(tabButtons) + 10);
size(currentTabPanel, width - 20, y(buttonPanel) - 10 - y(currentTabPanel)); size(currentTabPanel, width - 20, y(buttonPanel) - 10 - y(currentTabPanel));
} }
pos(tabButtons, 10, 20);
size(tabButtons, width - 20, 20);
} }
}); });
} }

View File

@@ -8,8 +8,11 @@ import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.List;
public class GuiHandler { public class GuiHandler {
private static final int BUTTON_REPLAY_EDITOR = 17890237; private static final int BUTTON_REPLAY_EDITOR = 17890237;
@@ -20,28 +23,31 @@ public class GuiHandler {
} }
public void register() { public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent
public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) { public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) {
if (!(event.getGui() instanceof GuiMainMenu)) { if (!(event.gui instanceof GuiMainMenu)) {
return; return;
} }
GuiButton button = new GuiButton(BUTTON_REPLAY_EDITOR, event.getGui().width / 2 + 2, @SuppressWarnings("unchecked")
event.getGui().height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayeditor")); List<GuiButton> buttonList = event.buttonList;
GuiButton button = new GuiButton(BUTTON_REPLAY_EDITOR, event.gui.width / 2 + 2,
event.gui.height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayeditor"));
button.width = button.width / 2 - 2; button.width = button.width / 2 - 2;
event.getButtonList().add(button); buttonList.add(button);
} }
@SubscribeEvent @SubscribeEvent
public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) { public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) {
if(!event.getButton().enabled) return; if(!event.button.enabled) return;
if (event.getGui() instanceof GuiMainMenu) { if (event.gui instanceof GuiMainMenu) {
if (event.getButton().id == BUTTON_REPLAY_EDITOR) { if (event.button.id == BUTTON_REPLAY_EDITOR) {
new GuiReplayEditor(GuiScreen.wrap(event.getGui()), mod.getCore()).display(); new GuiReplayEditor(GuiScreen.wrap(event.gui), mod.getCore()).display();
} }
} }
} }

View File

@@ -9,7 +9,7 @@ import de.johni0702.minecraft.gui.element.GuiImage;
import de.johni0702.minecraft.gui.element.IGuiImage; import de.johni0702.minecraft.gui.element.IGuiImage;
import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import net.minecraft.client.settings.GameSettings; import net.minecraft.client.settings.GameSettings;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
@@ -40,7 +40,7 @@ public class FullBrightness implements Extra {
} }
}); });
MinecraftForge.EVENT_BUS.register(this); FMLCommonHandler.instance().bus().register(this);
} }
@SubscribeEvent @SubscribeEvent

View File

@@ -17,7 +17,7 @@ import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.LayoutData; import de.johni0702.minecraft.gui.layout.LayoutData;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
@@ -35,7 +35,7 @@ public class HotkeyButtons implements Extra {
public void register(ReplayMod mod) throws Exception { public void register(ReplayMod mod) throws Exception {
this.mod = mod; this.mod = mod;
MinecraftForge.EVENT_BUS.register(this); FMLCommonHandler.instance().bus().register(this);
} }
@SubscribeEvent @SubscribeEvent

View File

@@ -1,106 +1,110 @@
package com.replaymod.extras; package com.replaymod.extras;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.online.ReplayModOnline;
import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.ApiException;
import lombok.RequiredArgsConstructor;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResourcePack; import net.minecraft.client.resources.IResourcePack;
import net.minecraft.client.resources.data.IMetadataSection; import net.minecraft.client.resources.data.IMetadataSection;
import net.minecraft.client.resources.data.MetadataSerializer; import net.minecraft.client.resources.data.IMetadataSerializer;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.io.IOUtils;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.ConnectException; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class LocalizationExtra implements Extra { public class LocalizationExtra implements Extra {
private ReplayModOnline module; private static final String ZIP_FILE_URL = "https://github.com/ReplayMod/Translations/archive/master.zip";
private static final String LANG_PREFIX = "Translations-master/";
@Override @Override
public void register(ReplayMod mod) throws Exception { public void register(ReplayMod mod) throws Exception {
this.module = ReplayModOnline.instance;
final Minecraft mc = mod.getMinecraft(); final Minecraft mc = mod.getMinecraft();
Thread localizedResourcePackLoader = new Thread(new Runnable() { if (Boolean.parseBoolean(System.getProperty("replaymod.offline", "false"))) {
@Override return;
public void run() { }
try { Thread localizedResourcePackLoader = new Thread(() -> {
try {
// Download zip of lang files
LOGGER.debug("Downloading languages from {}", ZIP_FILE_URL);
Map<String, byte[]> languages = new HashMap<>();
try (InputStream urlIn = new URL(ZIP_FILE_URL).openStream();
ZipInputStream in = new ZipInputStream(urlIn)) {
ZipEntry entry;
while ((entry = in.getNextEntry()) != null) {
String name = entry.getName();
if (!name.startsWith(LANG_PREFIX) || !name.endsWith(".lang")) {
continue;
}
name = name.substring(LANG_PREFIX.length());
languages.put(name, IOUtils.toByteArray(in));
LOGGER.debug("Added language file {}", name);
}
}
LOGGER.debug("Downloaded {} languages", languages.size());
// Add lang files as resource pack
mc.addScheduledTask(() -> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks; List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks;
defaultResourcePacks.add(new LocalizedResourcePack(module.getApiClient())); defaultResourcePacks.add(new LocalizedResourcePack(languages));
mc.addScheduledTask(new Runnable() { mc.getLanguageManager().onResourceManagerReload(mc.getResourceManager());
@Override LOGGER.debug("Added language files to resource packs and reloaded LanguageManager");
public void run() { });
mc.refreshResources(); } catch (Throwable t) {
} LOGGER.error("Loading localized resource pack:", t);
});
} catch(Exception e) {
e.printStackTrace();
}
} }
}, "localizedResourcePackLoader"); }, "localizedResourcePackLoader");
localizedResourcePackLoader.setDaemon(true);
localizedResourcePackLoader.start(); localizedResourcePackLoader.start();
} }
@RequiredArgsConstructor
public static class LocalizedResourcePack implements IResourcePack { public static class LocalizedResourcePack implements IResourcePack {
private final ApiClient apiClient; private final Pattern LANG_PATTERN = Pattern.compile("^lang/([.+].lang)$");
private Map<String, String> availableLanguages = new HashMap<>(); private final Map<String, byte[]> languages;
private boolean websiteAvailable = true;
public LocalizedResourcePack(Map<String, byte[]> languages) {
this.languages = languages;
}
@Override @Override
public InputStream getInputStream(ResourceLocation loc) { public InputStream getInputStream(ResourceLocation loc) {
if(!loc.getResourcePath().endsWith(".lang")) return null; if (!"replaymod".equals(loc.getResourceDomain())) return null;
String langcode = loc.getResourcePath().split("/")[1].split("\\.")[0]; Matcher matcher = LANG_PATTERN.matcher(loc.getResourcePath());
if(availableLanguages.containsKey(langcode)) return new ByteArrayInputStream(availableLanguages.get(langcode).getBytes(Charsets.UTF_8)); if (matcher.matches()) {
byte[] bytes = languages.get(matcher.group());
if (bytes != null) {
return new ByteArrayInputStream(bytes);
}
}
return null; return null;
} }
@Override @Override
public boolean resourceExists(ResourceLocation loc) { public boolean resourceExists(ResourceLocation loc) {
if(!(loc.getResourcePath().endsWith(".lang"))) return false; // Assumes that getInputStream returns a ByteArrayInputStream that doesn't need to be closed
String langcode = loc.getResourcePath().split("/")[1].split("\\.")[0]; return getInputStream(loc) != null;
if(availableLanguages.containsKey(langcode)) return true;
if(!websiteAvailable) return false;
try {
if (Boolean.parseBoolean(System.getProperty("replaymod.offline", "false"))) {
return false;
}
String lang = apiClient.getTranslation(langcode);
String prop = StringEscapeUtils.unescapeHtml4(lang);
availableLanguages.put(langcode, prop);
return true;
} catch (ApiException e) {
if (e.getError() == null || e.getError().getId() != 16) { // This language has not been translated
e.printStackTrace();
}
} catch(ConnectException ce) {
websiteAvailable = false;
ce.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
return false;
} }
@Override @Override
public Set<String> getResourceDomains() { public Set getResourceDomains() {
return ImmutableSet.of("minecraft", "replaymod"); return ImmutableSet.of("replaymod");
} }
@Override @Override
public <T extends IMetadataSection> T getPackMetadata(MetadataSerializer metadataSerializer, String metadataSectionName) throws IOException { public IMetadataSection getPackMetadata(IMetadataSerializer p_135058_1_, String p_135058_2_) throws IOException {
return null; return null;
} }

View File

@@ -43,7 +43,7 @@ public class OpenEyeExtra implements Extra {
} }
} }
private class OfferGui extends AbstractGuiScreen<OfferGui> { public class OfferGui extends AbstractGuiScreen<OfferGui> {
public final GuiScreen parent; public final GuiScreen parent;
public final GuiPanel textPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(3)) public final GuiPanel textPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(3))
.addElements(new VerticalLayout.Data(0.5), .addElements(new VerticalLayout.Data(0.5),
@@ -69,7 +69,7 @@ public class OpenEyeExtra implements Extra {
GuiPopup popup = new GuiPopup(OfferGui.this); GuiPopup popup = new GuiPopup(OfferGui.this);
new Thread(() -> { new Thread(() -> {
try { try {
File targetFile = new File("mods/" + Loader.MC_VERSION, "OpenEye.jar"); File targetFile = new File(mod.getMinecraft().mcDataDir, "mods/" + Loader.MC_VERSION + "/OpenEye.jar");
FileUtils.forceMkdir(targetFile.getParentFile()); FileUtils.forceMkdir(targetFile.getParentFile());
HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection(); HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection();
@@ -107,7 +107,7 @@ public class OpenEyeExtra implements Extra {
} }
} }
private static final class GuiPopup extends AbstractGuiPopup<GuiPopup> { public static final class GuiPopup extends AbstractGuiPopup<GuiPopup> {
GuiPopup(GuiContainer container) { GuiPopup(GuiContainer container) {
super(container); super(container);
popup.addElements(null, new GuiIndicator().setColor(Colors.BLACK)); popup.addElements(null, new GuiIndicator().setColor(Colors.BLACK));

View File

@@ -32,11 +32,11 @@ public class ReplayModExtras {
OpenEyeExtra.class OpenEyeExtra.class
); );
private Logger logger; public static Logger LOGGER;
@Mod.EventHandler @Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) { public void preInit(FMLPreInitializationEvent event) {
logger = event.getModLog(); LOGGER = event.getModLog();
} }
@Mod.EventHandler @Mod.EventHandler
@@ -46,7 +46,7 @@ public class ReplayModExtras {
Extra extra = cls.newInstance(); Extra extra = cls.newInstance();
extra.register(ReplayMod.instance); extra.register(ReplayMod.instance);
} catch (Throwable t) { } catch (Throwable t) {
logger.warn("Failed to load extra " + cls.getName() + ": ", t); LOGGER.warn("Failed to load extra " + cls.getName() + ": ", t);
} }
} }
} }

View File

@@ -15,6 +15,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.client.event.RenderHandEvent; import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
@@ -57,6 +58,7 @@ public class PlayerOverview implements Extra {
} }
}); });
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
RenderManager renderManager = mod.getMinecraft().getRenderManager(); RenderManager renderManager = mod.getMinecraft().getRenderManager();

View File

@@ -4,10 +4,15 @@ import com.replaymod.core.utils.Utils;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.*; import de.johni0702.minecraft.gui.container.GuiClickable;
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.container.GuiVerticalList;
import de.johni0702.minecraft.gui.element.GuiCheckbox; import de.johni0702.minecraft.gui.element.GuiCheckbox;
import de.johni0702.minecraft.gui.element.GuiImage; import de.johni0702.minecraft.gui.element.GuiImage;
import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTooltip;
import de.johni0702.minecraft.gui.element.IGuiCheckbox; import de.johni0702.minecraft.gui.element.IGuiCheckbox;
import de.johni0702.minecraft.gui.function.Closeable; import de.johni0702.minecraft.gui.function.Closeable;
import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.CustomLayout;
@@ -16,8 +21,7 @@ import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EnumPlayerModelParts; import net.minecraft.entity.player.EnumPlayerModelParts;
import net.minecraft.init.MobEffects; import net.minecraft.potion.Potion;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
@@ -37,21 +41,22 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public final GuiVerticalList playersScrollable = new GuiVerticalList(contentPanel) public final GuiVerticalList playersScrollable = new GuiVerticalList(contentPanel)
.setDrawSlider(true).setDrawShadow(true); .setDrawSlider(true).setDrawShadow(true);
public final GuiCheckbox saveCheckbox = new GuiCheckbox(contentPanel) public final GuiCheckbox saveCheckbox = new GuiCheckbox(contentPanel)
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.remembersettings.description"))
.setI18nLabel("replaymod.gui.playeroverview.remembersettings"); .setI18nLabel("replaymod.gui.playeroverview.remembersettings");
public final GuiCheckbox checkAll = new GuiCheckbox(contentPanel){ public final GuiCheckbox checkAll = new GuiCheckbox(contentPanel){
@Override @Override
public void onClick() { public void onClick() {
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F)); getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(BUTTON_SOUND, 1.0F));
playersScrollable.forEach(IGuiCheckbox.class).setChecked(true); playersScrollable.forEach(IGuiCheckbox.class).setChecked(true);
} }
}.setLabel("").setChecked(true); }.setLabel("").setChecked(true).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.showall"));
public final GuiCheckbox uncheckAll = new GuiCheckbox(contentPanel){ public final GuiCheckbox uncheckAll = new GuiCheckbox(contentPanel){
@Override @Override
public void onClick() { public void onClick() {
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F)); getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(BUTTON_SOUND, 1.0F));
playersScrollable.forEach(IGuiCheckbox.class).setChecked(false); playersScrollable.forEach(IGuiCheckbox.class).setChecked(false);
} }
}.setLabel("").setChecked(false); }.setLabel("").setChecked(false).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.hideall"));
{ {
setBackground(Background.NONE); setBackground(Background.NONE);
@@ -140,7 +145,7 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
} }
private static boolean isSpectator(EntityPlayer e) { private static boolean isSpectator(EntityPlayer e) {
return e.isInvisible() && e.getActivePotionEffect(MobEffects.INVISIBILITY) == null; return e.isInvisible() && e.getActivePotionEffect(Potion.invisibility) == null;
} }
private static final class PlayerComparator implements Comparator<EntityPlayer> { private static final class PlayerComparator implements Comparator<EntityPlayer> {

View File

@@ -17,8 +17,8 @@ public class YoutubeUpload implements Extra {
@SubscribeEvent @SubscribeEvent
public void onGuiOpen(GuiScreenEvent.InitGuiEvent.Post event) { public void onGuiOpen(GuiScreenEvent.InitGuiEvent.Post event) {
if (GuiScreen.from(event.getGui()) instanceof GuiRenderingDone) { if (GuiScreen.from(event.gui) instanceof GuiRenderingDone) {
GuiRenderingDone gui = (GuiRenderingDone) GuiScreen.from(event.getGui()); GuiRenderingDone gui = (GuiRenderingDone) GuiScreen.from(event.gui);
// Check if there already is a youtube button // Check if there already is a youtube button
if (gui.actionsPanel.getChildren().stream().anyMatch(it -> it instanceof YoutubeButton)) { if (gui.actionsPanel.getChildren().stream().anyMatch(it -> it instanceof YoutubeButton)) {
return; // Button already added return; // Button already added

View File

@@ -12,8 +12,8 @@ import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile; import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio; import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
@@ -74,7 +74,7 @@ public class ReplayModOnline {
} }
new GuiHandler(this).register(); new GuiHandler(this).register();
MinecraftForge.EVENT_BUS.register(this); FMLCommonHandler.instance().bus().register(this);
} }
@Mod.EventHandler @Mod.EventHandler
@@ -82,7 +82,7 @@ public class ReplayModOnline {
// Initial login prompt // Initial login prompt
if (!core.getSettingsRegistry().get(Setting.SKIP_LOGIN_PROMPT)) { if (!core.getSettingsRegistry().get(Setting.SKIP_LOGIN_PROMPT)) {
if (!isLoggedIn()) { if (!isLoggedIn()) {
new GuiLoginPrompt(apiClient, null, null, false).display(); core.runLater(() -> new GuiLoginPrompt(apiClient, GuiScreen.wrap(getMinecraft().currentScreen), null, false).display());
} }
} }
} }

View File

@@ -2,9 +2,7 @@ package com.replaymod.online.api.replay.holders;
public enum MinecraftVersion { public enum MinecraftVersion {
MC_1_8("Minecraft 1.8", "1.8"), MC_1_8("Minecraft 1.8", "1.8");
MC_1_9_4("Minecraft 1.9.4", "1.9.4"),
MC_1_10_2("Minecraft 1.10.2", "1.10.2");
private String niceName, apiName; private String niceName, apiName;

View File

@@ -21,7 +21,7 @@ public class GuiLoginPrompt extends AbstractGuiScreen<GuiLoginPrompt> {
private GuiLabel noAccountLabel = new GuiLabel(this).setI18nText("replaymod.gui.login.noacc"); private GuiLabel noAccountLabel = new GuiLabel(this).setI18nText("replaymod.gui.login.noacc");
private GuiLabel statusLabel = new GuiLabel(this); private GuiLabel statusLabel = new GuiLabel(this);
private GuiButton loginButton = new GuiButton(this).setI18nLabel("replaymod.gui.login").setSize(150, 20).setEnabled(false); private GuiButton loginButton = new GuiButton(this).setI18nLabel("replaymod.gui.login").setSize(150, 20).setEnabled(false);
private GuiButton cancelButton = new GuiButton(this).setI18nLabel("replaymod.gui.cancel").setSize(150, 20); public GuiButton cancelButton = new GuiButton(this).setI18nLabel("replaymod.gui.cancel").setSize(150, 20);
private GuiButton registerButton = new GuiButton(this).setI18nLabel("replaymod.gui.register").setSize(150, 20); private GuiButton registerButton = new GuiButton(this).setI18nLabel("replaymod.gui.register").setSize(150, 20);
private GuiTextField username = new GuiTextField(this).setSize(145, 20).setMaxLength(16).setFocused(true); private GuiTextField username = new GuiTextField(this).setSize(145, 20).setMaxLength(16).setFocused(true);
private GuiPasswordField password = new GuiPasswordField(this).setSize(145, 20) private GuiPasswordField password = new GuiPasswordField(this).setSize(145, 20)

View File

@@ -28,6 +28,7 @@ import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiImage; import de.johni0702.minecraft.gui.element.GuiImage;
import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTooltip;
import de.johni0702.minecraft.gui.element.IGuiButton; import de.johni0702.minecraft.gui.element.IGuiButton;
import de.johni0702.minecraft.gui.element.advanced.GuiResourceLoadingList; import de.johni0702.minecraft.gui.element.advanced.GuiResourceLoadingList;
import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.CustomLayout;
@@ -109,6 +110,7 @@ public class GuiReplayCenter extends GuiScreen {
public void run() { public void run() {
GuiReplayEntry selected = list.getSelected(); GuiReplayEntry selected = list.getSelected();
replayButtonPanel.forEach(IGuiButton.class).setEnabled(selected != null); replayButtonPanel.forEach(IGuiButton.class).setEnabled(selected != null);
replayButtonPanel.forEach(IGuiButton.class).setTooltip(null);
if (selected != null) { if (selected != null) {
int replayId = selected.fileInfo.getId(); int replayId = selected.fileInfo.getId();
boolean favorited = favoritedReplays.contains(replayId); boolean favorited = favoritedReplays.contains(replayId);
@@ -123,10 +125,22 @@ public class GuiReplayCenter extends GuiScreen {
favoriteButton.setI18nLabel("replaymod.gui.center." + (favorited ? "unfavorite" : "favorite")); favoriteButton.setI18nLabel("replaymod.gui.center." + (favorited ? "unfavorite" : "favorite"));
// Only allow button usage for either unfavorite or favorite after they've actually downloaded it // Only allow button usage for either unfavorite or favorite after they've actually downloaded it
favoriteButton.setEnabled(favorited || selected.downloaded); favoriteButton.setEnabled(favorited || selected.downloaded);
if (favoriteButton.isEnabled()) {
favoriteButton.setTooltip(null);
} else {
favoriteButton.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.center.downloadrequired"));
}
// Similar for like/dislike buttons // Similar for like/dislike buttons
likeButton.setEnabled(selected.downloaded); likeButton.setEnabled(selected.downloaded);
dislikeButton.setEnabled(selected.downloaded); dislikeButton.setEnabled(selected.downloaded);
if (likeButton.isEnabled()) {
likeButton.setTooltip(null);
dislikeButton.setTooltip(null);
} else {
likeButton.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.center.downloadrequired"));
dislikeButton.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.center.downloadrequired"));
}
likeButton.setI18nLabel("replaymod.gui." + (liked ? "removelike" : "like")); likeButton.setI18nLabel("replaymod.gui." + (liked ? "removelike" : "like"));
dislikeButton.setI18nLabel("replaymod.gui." + (disliked ? "removedislike" : "dislike")); dislikeButton.setI18nLabel("replaymod.gui." + (disliked ? "removedislike" : "dislike"));
} }

View File

@@ -17,7 +17,12 @@ import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.GuiContainer; import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel; import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.*; import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiCheckbox;
import de.johni0702.minecraft.gui.element.GuiImage;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTextField;
import de.johni0702.minecraft.gui.element.GuiTooltip;
import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu; import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu;
import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar; import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
import de.johni0702.minecraft.gui.element.advanced.GuiTextArea; import de.johni0702.minecraft.gui.element.advanced.GuiTextArea;
@@ -234,6 +239,8 @@ public class GuiUploadReplay extends GuiScreen {
}); });
validateInputs(); validateInputs();
name.onTextChanged(s -> validateInputs());
tags.onTextChanged(s -> validateInputs());
} }
public void validateInputs() { public void validateInputs() {

View File

@@ -14,9 +14,11 @@ import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.io.File; import java.io.File;
import java.util.List;
public class GuiHandler { public class GuiHandler {
private static final int BUTTON_REPLAY_CENTER = 17890236; private static final int BUTTON_REPLAY_CENTER = 17890236;
@@ -28,23 +30,26 @@ public class GuiHandler {
} }
public void register() { public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent
public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) { public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) {
if (!(event.getGui() instanceof GuiMainMenu)) { if (!(event.gui instanceof GuiMainMenu)) {
return; return;
} }
GuiButton button = new GuiButton(BUTTON_REPLAY_CENTER, event.getGui().width / 2 - 100, @SuppressWarnings("unchecked")
event.getGui().height / 4 + 10 + 4 * 24, I18n.format("replaymod.gui.replaycenter")); List<GuiButton> buttonList = event.buttonList;
event.getButtonList().add(button); GuiButton button = new GuiButton(BUTTON_REPLAY_CENTER, event.gui.width / 2 - 100,
event.gui.height / 4 + 10 + 4 * 24, I18n.format("replaymod.gui.replaycenter"));
buttonList.add(button);
} }
@SubscribeEvent @SubscribeEvent
public void injectIntoReplayViewer(GuiScreenEvent.InitGuiEvent.Post event) { public void injectIntoReplayViewer(GuiScreenEvent.InitGuiEvent.Post event) {
AbstractGuiScreen guiScreen = GuiScreen.from(event.getGui()); AbstractGuiScreen guiScreen = GuiScreen.from(event.gui);
if (!(guiScreen instanceof GuiReplayViewer)) { if (!(guiScreen instanceof GuiReplayViewer)) {
return; return;
} }
@@ -70,15 +75,15 @@ public class GuiHandler {
@SubscribeEvent @SubscribeEvent
public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) { public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) {
if(!event.getButton().enabled) return; if(!event.button.enabled) return;
if (event.getGui() instanceof GuiMainMenu) { if (event.gui instanceof GuiMainMenu) {
if (event.getButton().id == BUTTON_REPLAY_CENTER) { if (event.button.id == BUTTON_REPLAY_CENTER) {
GuiReplayCenter replayCenter = new GuiReplayCenter(mod); GuiReplayCenter replayCenter = new GuiReplayCenter(mod);
if (mod.isLoggedIn()) { if (mod.isLoggedIn()) {
replayCenter.display(); replayCenter.display();
} else { } else {
new GuiLoginPrompt(mod.getApiClient(), GuiScreen.wrap(event.getGui()), replayCenter, true).display(); new GuiLoginPrompt(mod.getApiClient(), GuiScreen.wrap(event.gui), replayCenter, true).display();
} }
} }
} }

View File

@@ -4,6 +4,7 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replaystudio.pathing.PathingRegistry; import com.replaymod.replaystudio.pathing.PathingRegistry;
import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Path;
@@ -11,7 +12,11 @@ import com.replaymod.replaystudio.pathing.path.Timeline;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.*; import de.johni0702.minecraft.gui.container.AbstractGuiClickableContainer;
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.container.GuiVerticalList;
import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTextField; import de.johni0702.minecraft.gui.element.GuiTextField;
@@ -41,9 +46,16 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable {
public final GuiButton overwriteButton = new GuiButton(buttonPanel).onClick(new Runnable() { public final GuiButton overwriteButton = new GuiButton(buttonPanel).onClick(new Runnable() {
@Override @Override
public void run() { public void run() {
timelines.put(selectedEntry.name, currentTimeline); GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this,
overwriteButton.setDisabled(); new GuiLabel().setI18nText("replaymod.gui.keyframerepo.overwrite").setColor(Colors.BLACK)
save(); ).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no");
Utils.addCallback(popup.getFuture(), doIt -> {
if (doIt) {
timelines.put(selectedEntry.name, currentTimeline);
overwriteButton.setDisabled();
save();
}
}, Throwable::printStackTrace);
} }
}).setSize(75, 20).setI18nLabel("replaymod.gui.overwrite").setDisabled(); }).setSize(75, 20).setI18nLabel("replaymod.gui.overwrite").setDisabled();
public final GuiButton saveAsButton = new GuiButton(buttonPanel).onClick(new Runnable() { public final GuiButton saveAsButton = new GuiButton(buttonPanel).onClick(new Runnable() {

View File

@@ -11,7 +11,7 @@ import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.replaystudio.pathing.path.Timeline;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -65,7 +65,7 @@ public abstract class AbstractTimelinePlayer {
} }
replayHandler.getReplaySender().setSyncModeAndWait(); replayHandler.getReplaySender().setSyncModeAndWait();
MinecraftForge.EVENT_BUS.register(this); FMLCommonHandler.instance().bus().register(this);
lastTime = 0; lastTime = 0;
mc.timer = new ReplayTimer(mc.timer); mc.timer = new ReplayTimer(mc.timer);
mc.timer.timerSpeed = 1; mc.timer.timerSpeed = 1;
@@ -87,7 +87,7 @@ public abstract class AbstractTimelinePlayer {
mc.timer = ((ReplayTimer) mc.timer).getWrapped(); mc.timer = ((ReplayTimer) mc.timer).getWrapped();
replayHandler.getReplaySender().setReplaySpeed(0); replayHandler.getReplaySender().setReplaySpeed(0);
replayHandler.getReplaySender().setAsyncMode(true); replayHandler.getReplaySender().setAsyncMode(true);
MinecraftForge.EVENT_BUS.unregister(this); FMLCommonHandler.instance().bus().unregister(this);
return; return;
} }
long time = getTimePassed(); long time = getTimePassed();

View File

@@ -2,7 +2,7 @@ package com.replaymod.pathing.player;
import com.replaymod.core.utils.WrappedTimer; import com.replaymod.core.utils.WrappedTimer;
import net.minecraft.util.Timer; import net.minecraft.util.Timer;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.Event; import net.minecraftforge.fml.common.eventhandler.Event;
/** /**
@@ -20,7 +20,7 @@ public class ReplayTimer extends WrappedTimer {
copy(this, state); // Save our current state copy(this, state); // Save our current state
super.updateTimer(); // Update current state super.updateTimer(); // Update current state
copy(state, this); // Restore our old state copy(state, this); // Restore our old state
MinecraftForge.EVENT_BUS.post(new UpdatedEvent()); FMLCommonHandler.instance().bus().post(new UpdatedEvent());
} }
public Timer getWrapped() { public Timer getWrapped() {

View File

@@ -4,8 +4,10 @@ import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Restrictions; import com.replaymod.core.utils.Restrictions;
import com.replaymod.recording.handler.ConnectionEventHandler; import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.recording.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
import net.minecraftforge.common.MinecraftForge; import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
@@ -51,12 +53,15 @@ public class ReplayModRecording {
@Mod.EventHandler @Mod.EventHandler
public void init(FMLInitializationEvent event) { public void init(FMLInitializationEvent event) {
EventBus bus = MinecraftForge.EVENT_BUS; EventBus bus = FMLCommonHandler.instance().bus();
bus.register(connectionEventHandler = new ConnectionEventHandler(logger, core)); bus.register(connectionEventHandler = new ConnectionEventHandler(logger, core));
NetworkRegistry.INSTANCE.newSimpleChannel(Restrictions.PLUGIN_CHANNEL); NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler());
} }
@ChannelHandler.Sharable
private static class RestrictionsChannelHandler extends ChannelDuplexHandler {}
public void initiateRecording(NetworkManager networkManager) { public void initiateRecording(NetworkManager networkManager) {
connectionEventHandler.onConnectedToServerEvent(networkManager); connectionEventHandler.onConnectedToServerEvent(networkManager);
} }

View File

@@ -40,7 +40,7 @@ public class GuiRecordingOverlay {
*/ */
@SubscribeEvent @SubscribeEvent
public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) { public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) {
if (event.getType() != RenderGameOverlayEvent.ElementType.ALL) return; if (event.type != RenderGameOverlayEvent.ElementType.ALL) return;
if (settingsRegistry.get(Setting.INDICATOR)) { if (settingsRegistry.get(Setting.INDICATOR)) {
FontRenderer fontRenderer = mc.fontRendererObj; FontRenderer fontRenderer = mc.fontRendererObj;
fontRenderer.drawString(I18n.format("replaymod.gui.recording").toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff); fontRenderer.drawString(I18n.format("replaymod.gui.recording").toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff);

View File

@@ -1,22 +1,24 @@
package com.replaymod.recording.handler; package com.replaymod.recording.handler;
import com.replaymod.recording.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.*; import net.minecraft.network.play.server.*;
import net.minecraft.network.play.server.S14PacketEntity.S17PacketEntityLookMove;
import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.EnumHand; import net.minecraft.util.BlockPos;
import net.minecraft.util.SoundCategory; import net.minecraft.util.MathHelper;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.minecart.MinecartInteractEvent; import net.minecraftforge.event.entity.minecart.MinecartInteractEvent;
import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent; import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent;
import net.minecraftforge.event.entity.player.PlayerUseItemEvent;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -30,24 +32,24 @@ public class RecordingEventHandler {
private final PacketListener packetListener; private final PacketListener packetListener;
private Double lastX, lastY, lastZ; private Double lastX, lastY, lastZ;
private ItemStack[] playerItems = new ItemStack[6]; private ItemStack[] playerItems = new ItemStack[5];
private int ticksSinceLastCorrection; private int ticksSinceLastCorrection;
private boolean wasSleeping; private boolean wasSleeping;
private int lastRiding = -1; private int lastRiding = -1;
private Integer rotationYawHeadBefore; private Integer rotationYawHeadBefore;
private boolean wasHandActive;
private EnumHand lastActiveHand;
public RecordingEventHandler(PacketListener packetListener) { public RecordingEventHandler(PacketListener packetListener) {
this.packetListener = packetListener; this.packetListener = packetListener;
} }
public void register() { public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
((RecordingEventSender) mc.renderGlobal).setRecordingEventHandler(this); ((RecordingEventSender) mc.renderGlobal).setRecordingEventHandler(this);
} }
public void unregister() { public void unregister() {
FMLCommonHandler.instance().bus().unregister(this);
MinecraftForge.EVENT_BUS.unregister(this); MinecraftForge.EVENT_BUS.unregister(this);
RecordingEventSender recordingEventSender = ((RecordingEventSender) mc.renderGlobal); RecordingEventSender recordingEventSender = ((RecordingEventSender) mc.renderGlobal);
if (recordingEventSender.getRecordingEventHandler() == this) { if (recordingEventSender.getRecordingEventHandler() == this) {
@@ -57,7 +59,7 @@ public class RecordingEventHandler {
public void onPlayerJoin() { public void onPlayerJoin() {
try { try {
packetListener.save(new SPacketSpawnPlayer(mc.thePlayer)); packetListener.save(spawnPlayer(mc.thePlayer));
} catch(Exception e1) { } catch(Exception e1) {
e1.printStackTrace(); e1.printStackTrace();
} }
@@ -65,28 +67,39 @@ public class RecordingEventHandler {
public void onPlayerRespawn() { public void onPlayerRespawn() {
try { try {
packetListener.save(new SPacketSpawnPlayer(mc.thePlayer)); packetListener.save(spawnPlayer(mc.thePlayer));
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
public void onClientSound(SoundEvent sound, SoundCategory category, private S0CPacketSpawnPlayer spawnPlayer(EntityPlayer player) {
double x, double y, double z, float volume, float pitch) {
try { try {
// Send to all other players in ServerWorldEventHandler#playSoundToAllNearExcept S0CPacketSpawnPlayer packet = new S0CPacketSpawnPlayer();
packetListener.save(new SPacketSoundEffect(sound, category, x, y, z, volume, pitch));
} catch(Exception e) {
e.printStackTrace();
}
}
public void onClientEffect(int type, BlockPos pos, int data) { ByteBuf bb = Unpooled.buffer();
try { PacketBuffer pb = new PacketBuffer(bb);
// Send to all other players in ServerWorldEventHandler#playEvent
packetListener.save(new SPacketEffect(type, pos, data, false)); pb.writeVarIntToBuffer(player.getEntityId());
pb.writeUuid(EntityPlayer.getUUID(player.getGameProfile()));
pb.writeInt(MathHelper.floor_double(player.posX * 32.0D));
pb.writeInt(MathHelper.floor_double(player.posY * 32.0D));
pb.writeInt(MathHelper.floor_double(player.posZ * 32.0D));
pb.writeByte((byte) ((int) (player.rotationYaw * 256.0F / 360.0F)));
pb.writeByte((byte) ((int) (player.rotationPitch * 256.0F / 360.0F)));
ItemStack itemstack = player.inventory.getCurrentItem();
pb.writeShort(itemstack == null ? 0 : Item.getIdFromItem(itemstack.getItem()));
player.getDataWatcher().writeTo(pb);
packet.readPacketData(pb);
return packet;
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
return null;
} }
} }
@@ -118,14 +131,19 @@ public class RecordingEventHandler {
lastZ = e.player.posZ; lastZ = e.player.posZ;
Packet packet; Packet packet;
if (force || Math.abs(dx) > 8.0 || Math.abs(dy) > 8.0 || Math.abs(dz) > 8.0) { if(force || Math.abs(dx) > 4.0 || Math.abs(dy) > 4.0 || Math.abs(dz) > 4.0) {
packet = new SPacketEntityTeleport(e.player); int x = MathHelper.floor_double(e.player.posX * 32.0D);
int y = MathHelper.floor_double(e.player.posY * 32.0D);
int z = MathHelper.floor_double(e.player.posZ * 32.0D);
byte yaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F));
byte pitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F));
packet = new S18PacketEntityTeleport(e.player.getEntityId(), x, y, z, yaw, pitch, e.player.onGround);
} else { } else {
byte newYaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F)); byte newYaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F));
byte newPitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F)); byte newPitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F));
packet = new SPacketEntity.S17PacketEntityLookMove(e.player.getEntityId(), packet = new S17PacketEntityLookMove(e.player.getEntityId(),
(short) Math.round(dx * 4096), (short) Math.round(dy * 4096), (short) Math.round(dz * 4096), (byte) Math.round(dx * 32), (byte) Math.round(dy * 32), (byte) Math.round(dz * 32),
newYaw, newPitch, e.player.onGround); newYaw, newPitch, e.player.onGround);
} }
@@ -135,16 +153,37 @@ public class RecordingEventHandler {
int rotationYawHead = ((int)(e.player.rotationYawHead * 256.0F / 360.0F)); int rotationYawHead = ((int)(e.player.rotationYawHead * 256.0F / 360.0F));
if(!Objects.equals(rotationYawHead, rotationYawHeadBefore)) { if(!Objects.equals(rotationYawHead, rotationYawHeadBefore)) {
packetListener.save(new SPacketEntityHeadLook(e.player, (byte) rotationYawHead)); S19PacketEntityHeadLook head = new S19PacketEntityHeadLook();
ByteBuf bb1 = Unpooled.buffer();
PacketBuffer pb1 = new PacketBuffer(bb1);
pb1.writeVarIntToBuffer(e.player.getEntityId());
pb1.writeByte(rotationYawHead);
head.readPacketData(pb1);
packetListener.save(head);
rotationYawHeadBefore = rotationYawHead; rotationYawHeadBefore = rotationYawHead;
} }
packetListener.save(new SPacketEntityVelocity(e.player.getEntityId(), e.player.motionX, e.player.motionY, e.player.motionZ)); S12PacketEntityVelocity vel = new S12PacketEntityVelocity(e.player.getEntityId(), e.player.motionX, e.player.motionY, e.player.motionZ);
packetListener.save(vel);
//Animation Packets //Animation Packets
//Swing Animation //Swing Animation
if (e.player.isSwingInProgress && e.player.swingProgressInt == -1) { if(e.player.swingProgressInt == 1) {
packetListener.save(new SPacketAnimation(e.player, e.player.swingingHand == EnumHand.MAIN_HAND ? 0 : 3)); S0BPacketAnimation pac = new S0BPacketAnimation();
ByteBuf bb = Unpooled.buffer();
PacketBuffer pb = new PacketBuffer(bb);
pb.writeVarIntToBuffer(e.player.getEntityId());
pb.writeByte(0);
pac.readPacketData(pb);
packetListener.save(pac);
} }
/* /*
@@ -168,62 +207,75 @@ public class RecordingEventHandler {
*/ */
//Inventory Handling //Inventory Handling
if (playerItems[0] != mc.thePlayer.getHeldItem(EnumHand.MAIN_HAND)) { if(playerItems[0] != mc.thePlayer.getHeldItem()) {
playerItems[0] = mc.thePlayer.getHeldItem(EnumHand.MAIN_HAND); playerItems[0] = mc.thePlayer.getHeldItem();
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.MAINHAND, playerItems[0])); S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 0, playerItems[0]);
packetListener.save(pee);
} }
if (playerItems[1] != mc.thePlayer.getHeldItem(EnumHand.OFF_HAND)) { if(playerItems[1] != mc.thePlayer.inventory.armorInventory[0]) {
playerItems[1] = mc.thePlayer.getHeldItem(EnumHand.OFF_HAND); playerItems[1] = mc.thePlayer.inventory.armorInventory[0];
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.OFFHAND, playerItems[1])); S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 1, playerItems[1]);
packetListener.save(pee);
} }
if (playerItems[2] != mc.thePlayer.inventory.armorInventory[0]) { if(playerItems[2] != mc.thePlayer.inventory.armorInventory[1]) {
playerItems[2] = mc.thePlayer.inventory.armorInventory[0]; playerItems[2] = mc.thePlayer.inventory.armorInventory[1];
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.FEET, playerItems[2])); S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 2, playerItems[2]);
packetListener.save(pee);
} }
if (playerItems[3] != mc.thePlayer.inventory.armorInventory[1]) { if(playerItems[3] != mc.thePlayer.inventory.armorInventory[2]) {
playerItems[3] = mc.thePlayer.inventory.armorInventory[1]; playerItems[3] = mc.thePlayer.inventory.armorInventory[2];
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.LEGS, playerItems[3])); S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 3, playerItems[3]);
packetListener.save(pee);
} }
if (playerItems[4] != mc.thePlayer.inventory.armorInventory[2]) { if(playerItems[4] != mc.thePlayer.inventory.armorInventory[3]) {
playerItems[4] = mc.thePlayer.inventory.armorInventory[2]; playerItems[4] = mc.thePlayer.inventory.armorInventory[3];
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.CHEST, playerItems[4])); S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 4, playerItems[4]);
} packetListener.save(pee);
if (playerItems[5] != mc.thePlayer.inventory.armorInventory[3]) {
playerItems[5] = mc.thePlayer.inventory.armorInventory[3];
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.HEAD, playerItems[5]));
} }
//Leaving Ride //Leaving Ride
if((!mc.thePlayer.isRiding() && lastRiding != -1) || if((!mc.thePlayer.isRiding() && lastRiding != -1) ||
(mc.thePlayer.isRiding() && lastRiding != mc.thePlayer.getRidingEntity().getEntityId())) { (mc.thePlayer.isRiding() && lastRiding != mc.thePlayer.ridingEntity.getEntityId())) {
if(!mc.thePlayer.isRiding()) { if(!mc.thePlayer.isRiding()) {
lastRiding = -1; lastRiding = -1;
} else { } else {
lastRiding = mc.thePlayer.getRidingEntity().getEntityId(); lastRiding = mc.thePlayer.ridingEntity.getEntityId();
} }
packetListener.save(new SPacketEntityAttach(e.player, e.player.getRidingEntity()));
S1BPacketEntityAttach pea = new S1BPacketEntityAttach();
ByteBuf buf = Unpooled.buffer();
PacketBuffer pbuf = new PacketBuffer(buf);
pbuf.writeInt(e.player.getEntityId());
pbuf.writeInt(lastRiding);
pbuf.writeBoolean(false);
pea.readPacketData(pbuf);
packetListener.save(pea);
} }
//Sleeping //Sleeping
if(!mc.thePlayer.isPlayerSleeping() && wasSleeping) { if(!mc.thePlayer.isPlayerSleeping() && wasSleeping) {
packetListener.save(new SPacketAnimation(e.player, 2)); S0BPacketAnimation pac = new S0BPacketAnimation();
wasSleeping = false;
}
// Active hand (e.g. eating, drinking, blocking) ByteBuf bb = Unpooled.buffer();
if (mc.thePlayer.isHandActive() ^ wasHandActive || mc.thePlayer.getActiveHand() != lastActiveHand) { PacketBuffer pb = new PacketBuffer(bb);
wasHandActive = mc.thePlayer.isHandActive();
lastActiveHand = mc.thePlayer.getActiveHand(); pb.writeVarIntToBuffer(e.player.getEntityId());
EntityDataManager dataManager = new EntityDataManager(null); pb.writeByte(2);
int state = (wasHandActive ? 1 : 0) | (lastActiveHand == EnumHand.OFF_HAND ? 2 : 0);
dataManager.register(EntityLiving.HAND_STATES, (byte) state); pac.readPacketData(pb);
packetListener.save(new SPacketEntityMetadata(mc.thePlayer.getEntityId(), dataManager, true));
packetListener.save(pac);
wasSleeping = false;
} }
} catch(Exception e1) { } catch(Exception e1) {
@@ -234,7 +286,27 @@ public class RecordingEventHandler {
@SubscribeEvent @SubscribeEvent
public void onPickupItem(ItemPickupEvent event) { public void onPickupItem(ItemPickupEvent event) {
try { try {
packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId())); packetListener.save(new S0DPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId()));
} catch(Exception e) {
e.printStackTrace();
}
}
@SubscribeEvent
public void onStartEating(PlayerUseItemEvent.Start event) {
try {
if(!event.entityPlayer.isEating()) return;
S0BPacketAnimation packet = new S0BPacketAnimation();
ByteBuf bb = Unpooled.buffer();
PacketBuffer pb = new PacketBuffer(bb);
pb.writeVarIntToBuffer(event.entityPlayer.getEntityId());
pb.writeByte(3);
packet.readPacketData(pb);
packetListener.save(packet);
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -243,11 +315,21 @@ public class RecordingEventHandler {
@SubscribeEvent @SubscribeEvent
public void onSleep(PlayerSleepInBedEvent event) { public void onSleep(PlayerSleepInBedEvent event) {
try { try {
if (event.getEntityPlayer() != mc.thePlayer) { if(event.entityPlayer != mc.thePlayer) {
return; return;
} }
packetListener.save(new SPacketUseBed(event.getEntityPlayer(), event.getPos())); S0APacketUseBed pub = new S0APacketUseBed();
ByteBuf buf = Unpooled.buffer();
PacketBuffer pbuf = new PacketBuffer(buf);
pbuf.writeVarIntToBuffer(event.entityPlayer.getEntityId());
pbuf.writeBlockPos(event.pos);
pub.readPacketData(pbuf);
packetListener.save(pub);
wasSleeping = true; wasSleeping = true;
@@ -259,13 +341,24 @@ public class RecordingEventHandler {
@SubscribeEvent @SubscribeEvent
public void enterMinecart(MinecartInteractEvent event) { public void enterMinecart(MinecartInteractEvent event) {
try { try {
if(event.getEntity() != mc.thePlayer) { if(event.player != mc.thePlayer) {
return; return;
} }
packetListener.save(new SPacketEntityAttach(event.getPlayer(), event.getMinecart())); S1BPacketEntityAttach pea = new S1BPacketEntityAttach();
lastRiding = event.getMinecart().getEntityId(); ByteBuf buf = Unpooled.buffer();
PacketBuffer pbuf = new PacketBuffer(buf);
pbuf.writeInt(event.player.getEntityId());
pbuf.writeInt(event.minecart.getEntityId());
pbuf.writeBoolean(false);
pea.readPacketData(pbuf);
packetListener.save(pea);
lastRiding = event.minecart.getEntityId();
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -274,7 +367,7 @@ public class RecordingEventHandler {
public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) { public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) {
EntityPlayer thePlayer = mc.thePlayer; EntityPlayer thePlayer = mc.thePlayer;
if (thePlayer != null && breakerId == thePlayer.getEntityId()) { if (thePlayer != null && breakerId == thePlayer.getEntityId()) {
packetListener.save(new SPacketBlockBreakAnim(breakerId, pos, progress)); packetListener.save(new S25PacketBlockBreakAnim(breakerId, pos, progress));
} }
} }

View File

@@ -4,8 +4,8 @@ import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.network.NetworkPlayerInfo; import net.minecraft.client.network.NetworkPlayerInfo;
import net.minecraft.network.play.server.SPacketPlayerListItem; import net.minecraft.network.play.server.S07PacketRespawn;
import net.minecraft.network.play.server.SPacketRespawn; import net.minecraft.network.play.server.S38PacketPlayerListItem;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -36,12 +36,12 @@ public abstract class MixinNetHandlerPlayClient {
* @param ci Callback info * @param ci Callback info
*/ */
@Inject(method = "handlePlayerListItem", at=@At("HEAD")) @Inject(method = "handlePlayerListItem", at=@At("HEAD"))
public void recordOwnJoin(SPacketPlayerListItem packet, CallbackInfo ci) { public void recordOwnJoin(S38PacketPlayerListItem packet, CallbackInfo ci) {
if (gameController.thePlayer == null) return; if (gameController.thePlayer == null) return;
RecordingEventHandler handler = getRecordingEventHandler(); RecordingEventHandler handler = getRecordingEventHandler();
if (handler != null && packet.getAction() == SPacketPlayerListItem.Action.ADD_PLAYER) { if (handler != null && packet.getAction() == S38PacketPlayerListItem.Action.ADD_PLAYER) {
for (SPacketPlayerListItem.AddPlayerData data : packet.getEntries()) { for (S38PacketPlayerListItem.AddPlayerData data : packet.getEntries()) {
if (data.getProfile() == null || data.getProfile().getId() == null) continue; if (data.getProfile() == null || data.getProfile().getId() == null) continue;
// Only add spawn packet for our own player and only if he isn't known yet // Only add spawn packet for our own player and only if he isn't known yet
if (data.getProfile().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId()) if (data.getProfile().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId())
@@ -55,12 +55,12 @@ public abstract class MixinNetHandlerPlayClient {
/** /**
* Record the own player entity respawning. * Record the own player entity respawning.
* We cannot use the {@link net.minecraftforge.event.entity.EntityJoinWorldEvent} because that would also include * We cannot use the {@link net.minecraftforge.event.entity.EntityJoinWorldEvent} because that would also include
* the first spawn which is already handled by {@link #recordOwnJoin(SPacketPlayerListItem, CallbackInfo)}. * the first spawn which is already handled by {@link #recordOwnJoin(S38PacketPlayerListItem, CallbackInfo)}.
* @param packet The packet * @param packet The packet
* @param ci Callback info * @param ci Callback info
*/ */
@Inject(method = "handleRespawn", at=@At("RETURN")) @Inject(method = "handleRespawn", at=@At("RETURN"))
public void recordOwnRespawn(SPacketRespawn packet, CallbackInfo ci) { public void recordOwnRespawn(S07PacketRespawn packet, CallbackInfo ci) {
RecordingEventHandler handler = getRecordingEventHandler(); RecordingEventHandler handler = getRecordingEventHandler();
if (handler != null) { if (handler != null) {
handler.onPlayerRespawn(); handler.onPlayerRespawn();

View File

@@ -1,28 +0,0 @@
package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
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.Redirect;
@Mixin(PlayerControllerMP.class)
public abstract class MixinPlayerControllerMP implements RecordingEventHandler.RecordingEventSender {
@Shadow
private Minecraft mc;
// Redirects the call to playEvent without the initial player argument to the method with that argument
// The new method will then play it and (if applicable) record it. (See MixinWorldClient)
// This is necessary for the block break event (particles and sound) to be recorded. Otherwise it looks like the
// event was emitted because of a packet (player will be null) and not as it actually was (by the player).
@Redirect(method = "onPlayerDestroyBlock", at = @At(value = "INVOKE",
target = "Lnet/minecraft/world/World;playEvent(ILnet/minecraft/util/math/BlockPos;I)V"))
public void replayModRecording_playEvent_fixed(World world, int type, BlockPos pos, int data) {
world.playEvent(mc.thePlayer, type, pos, data);
}
}

View File

@@ -2,7 +2,7 @@ package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.RecordingEventHandler; import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.renderer.RenderGlobal; import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.BlockPos;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;

View File

@@ -1,62 +0,0 @@
package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.profiler.Profiler;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.storage.ISaveHandler;
import net.minecraft.world.storage.WorldInfo;
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(WorldClient.class)
public abstract class MixinWorldClient extends World implements RecordingEventHandler.RecordingEventSender {
@Shadow
private Minecraft mc;
protected MixinWorldClient(ISaveHandler saveHandlerIn, WorldInfo info, WorldProvider providerIn, Profiler profilerIn, boolean client) {
super(saveHandlerIn, info, providerIn, profilerIn, client);
}
private RecordingEventHandler replayModRecording_getRecordingEventHandler() {
return ((RecordingEventHandler.RecordingEventSender) mc.renderGlobal).getRecordingEventHandler();
}
// Sounds that are emitted by thePlayer no longer take the long way over the server
// but are instead played directly by the client. The server only sends these sounds to
// other clients so we have to record them manually.
// E.g. Block place sounds
@Inject(method = "playSound", at = @At("HEAD"))
public void replayModRecording_recordClientSound(EntityPlayer player, double x, double y, double z, SoundEvent sound, SoundCategory category,
float volume, float pitch, CallbackInfo ci) {
if (player == mc.thePlayer) {
RecordingEventHandler handler = replayModRecording_getRecordingEventHandler();
if (handler != null) {
handler.onClientSound(sound, category, x, y, z, volume, pitch);
}
}
}
// Same goes for level events (also called effects). E.g. door open, block break, etc.
// These are handled in the World class, so we override the method in WorldClient and add our special handling.
@Override
public void playEvent(EntityPlayer player, int type, BlockPos pos, int data) {
if (player == mc.thePlayer) {
// We caused this event, the server won't send it to us
RecordingEventHandler handler = replayModRecording_getRecordingEventHandler();
if (handler != null) {
handler.onClientEffect(type, pos, data);
}
}
super.playEvent(player, type, pos, data);
}
}

View File

@@ -9,24 +9,21 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.entity.DataWatcher;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.network.EnumConnectionState; import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.EnumPacketDirection; import net.minecraft.network.EnumPacketDirection;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.network.play.server.*; import net.minecraft.network.play.server.*;
import net.minecraft.util.text.TextComponentString; import net.minecraft.util.ChatComponentText;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.*;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -87,8 +84,8 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
public void save(Packet packet) { public void save(Packet packet) {
try { try {
if(packet instanceof SPacketSpawnPlayer) { if(packet instanceof S0CPacketSpawnPlayer) {
UUID uuid = ((SPacketSpawnPlayer) packet).getUniqueId(); UUID uuid = ((S0CPacketSpawnPlayer) packet).getPlayer();
Set<String> uuids = new HashSet<>(Arrays.asList(metaData.getPlayers())); Set<String> uuids = new HashSet<>(Arrays.asList(metaData.getPlayers()));
uuids.add(uuid.toString()); uuids.add(uuid.toString());
metaData.setPlayers(uuids.toArray(new String[uuids.size()])); metaData.setPlayers(uuids.toArray(new String[uuids.size()]));
@@ -154,16 +151,16 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
try { try {
Packet packet = (Packet) msg; Packet packet = (Packet) msg;
if(packet instanceof SPacketCollectItem) { if(packet instanceof S0DPacketCollectItem) {
if(mc.thePlayer != null || if(mc.thePlayer != null ||
((SPacketCollectItem) packet).getEntityID() == mc.thePlayer.getEntityId()) { ((S0DPacketCollectItem) packet).getEntityID() == mc.thePlayer.getEntityId()) {
super.channelRead(ctx, msg); super.channelRead(ctx, msg);
return; return;
} }
} }
if (packet instanceof SPacketResourcePackSend) { if (packet instanceof S48PacketResourcePackSend) {
save(resourcePackRecorder.handleResourcePack((SPacketResourcePackSend) packet)); save(resourcePackRecorder.handleResourcePack((S48PacketResourcePackSend) packet));
return; return;
} }
@@ -176,10 +173,10 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
save(packet); save(packet);
if (packet instanceof SPacketCustomPayload) { if (packet instanceof S3FPacketCustomPayload) {
SPacketCustomPayload p = (SPacketCustomPayload) packet; S3FPacketCustomPayload p = (S3FPacketCustomPayload) packet;
if (Restrictions.PLUGIN_CHANNEL.equals(p.getChannelName())) { if (Restrictions.PLUGIN_CHANNEL.equals(p.getChannelName())) {
packet = new SPacketDisconnect(new TextComponentString("Please update to view this replay.")); packet = new S40PacketDisconnect(new ChatComponentText("Please update to view this replay."));
save(packet); save(packet);
} }
} }
@@ -192,31 +189,27 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
super.channelRead(ctx, msg); super.channelRead(ctx, msg);
} }
private <T> void DataManager_set(EntityDataManager dataManager, EntityDataManager.DataEntry<T> entry) {
dataManager.register(entry.getKey(), entry.getValue());
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private byte[] getPacketData(Packet packet) throws IOException { private byte[] getPacketData(Packet packet) throws IOException {
if (packet instanceof SPacketSpawnMob) { if(packet instanceof S0FPacketSpawnMob) {
SPacketSpawnMob p = (SPacketSpawnMob) packet; S0FPacketSpawnMob p = (S0FPacketSpawnMob) packet;
if (p.dataManager == null) { if (p.field_149043_l == null) {
p.dataManager = new EntityDataManager(null); p.field_149043_l = new DataWatcher(null);
if (p.getDataManagerEntries() != null) { if(p.func_149027_c() != null) {
for (EntityDataManager.DataEntry<?> entry : p.getDataManagerEntries()) { for(DataWatcher.WatchableObject wo : (List<DataWatcher.WatchableObject>) p.func_149027_c()) {
DataManager_set(p.dataManager, entry); p.field_149043_l.addObject(wo.getDataValueId(), wo.getObject());
} }
} }
} }
} }
if (packet instanceof SPacketSpawnPlayer) { if(packet instanceof S0CPacketSpawnPlayer) {
SPacketSpawnPlayer p = (SPacketSpawnPlayer) packet; S0CPacketSpawnPlayer p = (S0CPacketSpawnPlayer) packet;
if (p.watcher == null) { if (p.watcher == null) {
p.watcher = new EntityDataManager(null); p.watcher = new DataWatcher(null);
if (p.getDataManagerEntries() != null) { if(p.func_148944_c() != null) {
for (EntityDataManager.DataEntry<?> entry : p.getDataManagerEntries()) { for(DataWatcher.WatchableObject wo : p.func_148944_c()) {
DataManager_set(p.watcher, entry); p.watcher.addObject(wo.getDataValueId(), wo.getObject());
} }
} }
} }

View File

@@ -15,8 +15,8 @@ import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.client.resources.ResourcePackRepository; import net.minecraft.client.resources.ResourcePackRepository;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.client.CPacketResourcePackStatus; import net.minecraft.network.play.client.C19PacketResourcePackStatus;
import net.minecraft.network.play.server.SPacketResourcePackSend; import net.minecraft.network.play.server.S48PacketResourcePackSend;
import net.minecraft.util.HttpUtil; import net.minecraft.util.HttpUtil;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@@ -74,9 +74,9 @@ public class ResourcePackRecorder {
} }
} }
public synchronized SPacketResourcePackSend handleResourcePack(SPacketResourcePackSend packet) { public synchronized S48PacketResourcePackSend handleResourcePack(S48PacketResourcePackSend packet) {
final int requestId = nextRequestId++; final int requestId = nextRequestId++;
final NetHandlerPlayClient netHandler = mc.getConnection(); final NetHandlerPlayClient netHandler = mc.getNetHandler();
final NetworkManager netManager = netHandler.getNetworkManager(); final NetworkManager netManager = netHandler.getNetworkManager();
final String url = packet.getURL(); final String url = packet.getURL();
final String hash = packet.getHash(); final String hash = packet.getHash();
@@ -87,60 +87,60 @@ public class ResourcePackRecorder {
final File levelDir = new File(savesDir, levelName); final File levelDir = new File(savesDir, levelName);
if (levelDir.isFile()) { if (levelDir.isFile()) {
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.ACCEPTED)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
Futures.addCallback(mc.getResourcePackRepository().setResourcePackInstance(levelDir), new FutureCallback<Object>() { Futures.addCallback(mc.getResourcePackRepository().setResourcePackInstance(levelDir), new FutureCallback<Object>() {
@Override @Override
public void onSuccess(Object result) { public void onSuccess(Object result) {
recordResourcePack(levelDir, requestId); recordResourcePack(levelDir, requestId);
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.SUCCESSFULLY_LOADED)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
} }
@Override @Override
public void onFailure(@Nonnull Throwable throwable) { public void onFailure(@Nonnull Throwable throwable) {
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.FAILED_DOWNLOAD)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD));
} }
}); });
} else { } else {
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.FAILED_DOWNLOAD)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD));
} }
} else { } else {
final ServerData serverData = mc.getCurrentServerData(); final ServerData serverData = mc.getCurrentServerData();
if (serverData != null && serverData.getResourceMode() == ServerData.ServerResourceMode.ENABLED) { if (serverData != null && serverData.getResourceMode() == ServerData.ServerResourceMode.ENABLED) {
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.ACCEPTED)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
downloadResourcePackFuture(requestId, url, hash); downloadResourcePackFuture(requestId, url, hash);
} else if (serverData != null && serverData.getResourceMode() != ServerData.ServerResourceMode.PROMPT) { } else if (serverData != null && serverData.getResourceMode() != ServerData.ServerResourceMode.PROMPT) {
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.DECLINED)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED));
} else { } else {
mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo((result, id) -> { mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo((result, id) -> {
if (serverData != null) { if (serverData != null) {
serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED); serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED);
} }
if (result) { if (result) {
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.ACCEPTED)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
downloadResourcePackFuture(requestId, url, hash); downloadResourcePackFuture(requestId, url, hash);
} else { } else {
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.DECLINED)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED));
} }
ServerList.saveSingleServer(serverData); ServerList.func_147414_b(serverData);
mc.displayGuiScreen(null); mc.displayGuiScreen(null);
}, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0))); }, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0)));
} }
} }
return new SPacketResourcePackSend("replay://" + requestId, ""); return new S48PacketResourcePackSend("replay://" + requestId, "");
} }
private void downloadResourcePackFuture(int requestId, String url, final String hash) { private void downloadResourcePackFuture(int requestId, String url, final String hash) {
Futures.addCallback(downloadResourcePack(requestId, url, hash), new FutureCallback() { Futures.addCallback(downloadResourcePack(requestId, url, hash), new FutureCallback() {
@Override @Override
public void onSuccess(Object result) { public void onSuccess(Object result) {
mc.getConnection().sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.SUCCESSFULLY_LOADED)); mc.getNetHandler().addToSendQueue(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
} }
@Override @Override
public void onFailure(@Nonnull Throwable throwable) { public void onFailure(@Nonnull Throwable throwable) {
mc.getConnection().sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.FAILED_DOWNLOAD)); mc.getNetHandler().addToSendQueue(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD));
} }
}); });
} }
@@ -190,7 +190,7 @@ public class ResourcePackRecorder {
Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen))); Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen)));
Map<String, String> sessionInfo = ResourcePackRepository.func_190115_a(); Map<String, String> sessionInfo = Minecraft.getSessionInfo();
repo.downloadingPacks = HttpUtil.downloadResourcePack(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy()); repo.downloadingPacks = HttpUtil.downloadResourcePack(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
Futures.addCallback(repo.downloadingPacks, new FutureCallback<Object>() { Futures.addCallback(repo.downloadingPacks, new FutureCallback<Object>() {
@Override @Override

View File

@@ -5,8 +5,8 @@ import com.replaymod.render.utils.RenderJob;
import com.replaymod.replay.events.ReplayCloseEvent; import com.replaymod.replay.events.ReplayCloseEvent;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException; import net.minecraft.util.ReportedException;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -45,7 +45,7 @@ public class ReplayModRender {
core = ReplayMod.instance; core = ReplayMod.instance;
configuration = new Configuration(event.getSuggestedConfigurationFile()); configuration = new Configuration(event.getSuggestedConfigurationFile());
MinecraftForge.EVENT_BUS.register(this); FMLCommonHandler.instance().bus().register(this);
core.getSettingsRegistry().register(Setting.class); core.getSettingsRegistry().register(Setting.class);
} }

View File

@@ -10,7 +10,7 @@ import net.minecraft.client.renderer.chunk.RenderChunk;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Iterator; import java.util.Iterator;
import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.BlockingQueue;
public class ChunkLoadingRenderGlobal { public class ChunkLoadingRenderGlobal {
@@ -27,10 +27,10 @@ public class ChunkLoadingRenderGlobal {
this.renderWorker = new CustomChunkRenderWorker(renderDispatcher, new RegionRenderCacheBuilder()); this.renderWorker = new CustomChunkRenderWorker(renderDispatcher, new RegionRenderCacheBuilder());
int workerThreads = renderDispatcher.listThreadedWorkers.size(); int workerThreads = renderDispatcher.listThreadedWorkers.size();
PriorityBlockingQueue<ChunkCompileTaskGenerator> queueChunkUpdates = renderDispatcher.queueChunkUpdates; BlockingQueue<ChunkCompileTaskGenerator> queueChunkUpdates = renderDispatcher.queueChunkUpdates;
workerJailingQueue = new JailingQueue<>(queueChunkUpdates); workerJailingQueue = new JailingQueue<>(queueChunkUpdates);
renderDispatcher.queueChunkUpdates = workerJailingQueue; renderDispatcher.queueChunkUpdates = workerJailingQueue;
ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null, 0); ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null);
element.finish(); element.finish();
for (int i = 0; i < workerThreads; i++) { for (int i = 0; i < workerThreads; i++) {
queueChunkUpdates.add(element); queueChunkUpdates.add(element);
@@ -68,7 +68,7 @@ public class ChunkLoadingRenderGlobal {
renderDispatcher.updateChunkNow(renderchunk); renderDispatcher.updateChunkNow(renderchunk);
renderchunk.clearNeedsUpdate(); renderchunk.setNeedsUpdate(false);
iterator.remove(); iterator.remove();
} }
} }

View File

@@ -0,0 +1,47 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(EffectRenderer.class)
public abstract class MixinEffectRenderer {
@Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderNormalParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(fx, worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
@Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderLitParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(fx, worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
private void renderParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && handler.omnidirectional) {
// Align all particles towards the camera
double dx = fx.prevPosX + (fx.posX - fx.prevPosX) * partialTicks - view.posX;
double dy = fx.prevPosY + (fx.posY - fx.prevPosY) * partialTicks - view.posY;
double dz = fx.prevPosZ + (fx.posZ - fx.prevPosZ) * partialTicks - view.posZ;
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
double yaw = -Math.atan2(dx, dz);
rotX = (float) Math.cos(yaw);
rotZ = (float) Math.sin(yaw);
rotXZ = (float) Math.cos(pitch);
rotYZ = (float) (-rotZ * Math.sin(pitch));
rotXY = (float) (rotX * Math.sin(pitch));
}
fx.renderParticle(worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
}

View File

@@ -12,7 +12,7 @@ import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.settings.GameSettings; import net.minecraft.client.settings.GameSettings;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.MovingObjectPosition;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.util.ReadableColor; import org.lwjgl.util.ReadableColor;
import org.lwjgl.util.glu.Project; import org.lwjgl.util.glu.Project;
@@ -118,10 +118,10 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
@Shadow @Shadow
public abstract void renderHand(float partialTicks, int renderPass); public abstract void renderHand(float partialTicks, int renderPass);
@Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/math/RayTraceResult;IF)V")) @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/MovingObjectPosition;IF)V"))
private void replayModRender_drawSelectionBox(RenderGlobal instance, EntityPlayer player, RayTraceResult rtr, int alwaysZero, float partialTicks) { private void replayModRender_drawSelectionBox(RenderGlobal instance, EntityPlayer player, MovingObjectPosition mop, int alwaysZero, float partialTicks) {
if (replayModRender_handler == null) { if (replayModRender_handler == null) {
instance.drawSelectionBox(player, rtr, alwaysZero, partialTicks); instance.drawSelectionBox(player, mop, alwaysZero, partialTicks);
} }
} }

View File

@@ -1,47 +0,0 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.client.renderer.VertexBuffer;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ParticleManager.class)
public abstract class MixinParticleManager {
@Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/Particle;renderParticle(Lnet/minecraft/client/renderer/VertexBuffer;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderNormalParticle(Particle particle, VertexBuffer vertexBuffer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(particle, vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
@Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/Particle;renderParticle(Lnet/minecraft/client/renderer/VertexBuffer;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderLitParticle(Particle particle, VertexBuffer vertexBuffer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(particle, vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
private void renderParticle(Particle particle, VertexBuffer vertexBuffer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && handler.omnidirectional) {
// Align all particles towards the camera
double dx = particle.prevPosX + (particle.posX - particle.prevPosX) * partialTicks - view.posX;
double dy = particle.prevPosY + (particle.posY - particle.prevPosY) * partialTicks - view.posY;
double dz = particle.prevPosZ + (particle.posZ - particle.prevPosZ) * partialTicks - view.posZ;
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
double yaw = -Math.atan2(dx, dz);
rotX = (float) Math.cos(yaw);
rotZ = (float) Math.sin(yaw);
rotXZ = (float) Math.cos(pitch);
rotYZ = (float) (-rotZ * Math.sin(pitch));
rotXY = (float) (rotX * Math.sin(pitch));
}
particle.renderParticle(vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
}

View File

@@ -7,14 +7,15 @@ import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; 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.CallbackInfoReturnable;
@Mixin(Render.class) @Mixin(Render.class)
public abstract class MixinRender { public abstract class MixinRender {
@Inject(method = "renderLivingLabel", at = @At("HEAD"), cancellable = true) @Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true)
private void replayModRender_areAllNamesHidden(Entity entityIn, String str, double x, double y, double z, int maxDistance, CallbackInfo ci) { private void replayModRender_areAllNamesHidden(Entity entity, CallbackInfoReturnable<Boolean> ci) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler(); EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && !handler.getSettings().isRenderNameTags()) { if (handler != null && !handler.getSettings().isRenderNameTags()) {
ci.setReturnValue(false);
ci.cancel(); ci.cancel();
} }
} }

View File

@@ -3,13 +3,16 @@ package com.replaymod.render.mixin;
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import net.minecraft.client.renderer.RenderGlobal; import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.client.renderer.chunk.RenderChunk;
import net.minecraft.client.renderer.culling.ICamera; import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.BlockPos;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; 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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(RenderGlobal.class) @Mixin(RenderGlobal.class)
public abstract class MixinRenderGlobal { public abstract class MixinRenderGlobal {
@@ -34,8 +37,6 @@ public abstract class MixinRenderGlobal {
do { do {
setupTerrain(viewEntity, partialTicks, camera, replayModRender_hook.nextFrameId(), playerSpectator); setupTerrain(viewEntity, partialTicks, camera, replayModRender_hook.nextFrameId(), playerSpectator);
replayModRender_hook.updateChunks();
} while (displayListEntitiesDirty); } while (displayListEntitiesDirty);
displayListEntitiesDirty = true; displayListEntitiesDirty = true;
@@ -45,6 +46,13 @@ public abstract class MixinRenderGlobal {
} }
} }
@Inject(method = "isPositionInRenderChunk", at = @At("HEAD"), cancellable = true)
public void replayModRender_isPositionInRenderChunk(BlockPos pos, RenderChunk chunk, CallbackInfoReturnable<Boolean> ci) {
if (replayModRender_hook != null) {
ci.setReturnValue(true);
}
}
@Inject(method = "updateChunks", at = @At("HEAD"), cancellable = true) @Inject(method = "updateChunks", at = @At("HEAD"), cancellable = true)
public void replayModRender_updateChunks(long finishTimeNano, CallbackInfo ci) { public void replayModRender_updateChunks(long finishTimeNano, CallbackInfo ci) {
if (replayModRender_hook != null) { if (replayModRender_hook != null) {

View File

@@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; 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.CallbackInfoReturnable;
@Mixin(RenderManager.class) @Mixin(RenderManager.class)
public abstract class MixinRenderManager { public abstract class MixinRenderManager {
@@ -19,7 +19,7 @@ public abstract class MixinRenderManager {
private float playerViewY; private float playerViewY;
@Inject(method = "doRenderEntity", at = @At("HEAD")) @Inject(method = "doRenderEntity", at = @At("HEAD"))
private void replayModRender_reorientForCubicRendering(Entity entity, double dx, double dy, double dz, float iDoNotKnow, float partialTicks, boolean iDoNotCare, CallbackInfo ci) { private void replayModRender_reorientForCubicRendering(Entity entity, double dx, double dy, double dz, float iDoNotKnow, float partialTicks, boolean iDoNotCare, CallbackInfoReturnable ci) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler(); EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && handler.omnidirectional) { if (handler != null && handler.omnidirectional) {
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)); double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));

View File

@@ -0,0 +1,21 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.RendererLivingEntity;
import net.minecraft.entity.EntityLivingBase;
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(RendererLivingEntity.class)
public abstract class MixinRendererLivingEntity {
@Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true)
private void replayModRender_areAllNamesHidden(EntityLivingBase entity, CallbackInfoReturnable<Boolean> ci) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && !handler.getSettings().isRenderNameTags()) {
ci.setReturnValue(false); //this calls the cancel method
}
}
}

View File

@@ -1,5 +1,6 @@
package com.replaymod.render.rendering; package com.replaymod.render.rendering;
import com.replaymod.core.ReplayMod;
import com.replaymod.pathing.player.AbstractTimelinePlayer; import com.replaymod.pathing.player.AbstractTimelinePlayer;
import com.replaymod.pathing.player.ReplayTimer; import com.replaymod.pathing.player.ReplayTimer;
import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.pathing.properties.TimestampProperty;
@@ -13,18 +14,17 @@ import com.replaymod.render.gui.GuiRenderingDone;
import com.replaymod.render.gui.GuiVideoRenderer; import com.replaymod.render.gui.GuiVideoRenderer;
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import com.replaymod.render.metadata.MetadataInjector; import com.replaymod.render.metadata.MetadataInjector;
import com.replaymod.render.utils.SoundHandler;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Keyframe; import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.replaystudio.pathing.path.Timeline;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.SoundCategory;
import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.shader.Framebuffer; import net.minecraft.client.shader.Framebuffer;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.Timer; import net.minecraft.util.Timer;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLCommonHandler;
import org.lwjgl.input.Mouse; import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
@@ -54,7 +54,7 @@ public class VideoRenderer implements RenderInfo {
private int fps; private int fps;
private boolean mouseWasGrabbed; private boolean mouseWasGrabbed;
private boolean debugInfoWasShown; private boolean debugInfoWasShown;
private Map<SoundCategory, Float> originalSoundLevels; private Map originalSoundLevels;
private TimelinePlayer timelinePlayer; private TimelinePlayer timelinePlayer;
private Future<Void> timelinePlayerFuture; private Future<Void> timelinePlayerFuture;
@@ -90,7 +90,7 @@ public class VideoRenderer implements RenderInfo {
* @return {@code true} if rendering was successful, {@code false} if the user aborted rendering (or the window was closed) * @return {@code true} if rendering was successful, {@code false} if the user aborted rendering (or the window was closed)
*/ */
public boolean renderVideo() { public boolean renderVideo() {
MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Pre(this)); FMLCommonHandler.instance().bus().post(new ReplayRenderEvent.Pre(this));
setup(); setup();
@@ -136,7 +136,7 @@ public class VideoRenderer implements RenderInfo {
finish(); finish();
MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Post(this)); FMLCommonHandler.instance().bus().post(new ReplayRenderEvent.Post(this));
return !cancelled; return !cancelled;
} }
@@ -196,9 +196,9 @@ public class VideoRenderer implements RenderInfo {
for (SoundCategory category : SoundCategory.values()) { for (SoundCategory category : SoundCategory.values()) {
mutedSounds.put(category, 0f); mutedSounds.put(category, 0f);
} }
originalSoundLevels = mc.gameSettings.soundLevels; originalSoundLevels = mc.gameSettings.mapSoundLevels;
mutedSounds.put(SoundCategory.MASTER, originalSoundLevels.get(SoundCategory.MASTER)); mutedSounds.put(SoundCategory.MASTER, (Float) originalSoundLevels.get(SoundCategory.MASTER));
mc.gameSettings.soundLevels = mutedSounds; mc.gameSettings.mapSoundLevels = mutedSounds;
fps = settings.getFramesPerSecond(); fps = settings.getFramesPerSecond();
@@ -219,6 +219,9 @@ public class VideoRenderer implements RenderInfo {
updateDisplaySize(); updateDisplaySize();
ScaledResolution scaled = new ScaledResolution(mc);
gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight());
chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.renderGlobal); chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.renderGlobal);
// Set up our own framebuffer to render the GUI to // Set up our own framebuffer to render the GUI to
@@ -239,13 +242,13 @@ public class VideoRenderer implements RenderInfo {
if (mouseWasGrabbed) { if (mouseWasGrabbed) {
mc.mouseHelper.grabMouseCursor(); mc.mouseHelper.grabMouseCursor();
} }
mc.gameSettings.soundLevels = originalSoundLevels; mc.gameSettings.mapSoundLevels = originalSoundLevels;
mc.displayGuiScreen(null); mc.displayGuiScreen(null);
if (chunkLoadingRenderGlobal != null) { if (chunkLoadingRenderGlobal != null) {
chunkLoadingRenderGlobal.uninstall(); chunkLoadingRenderGlobal.uninstall();
} }
new SoundHandler().playRenderSuccessSound(); ReplayMod.soundHandler.playRenderSuccessSound();
new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display(); new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();

View File

@@ -2,18 +2,15 @@ package com.replaymod.render.utils;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import java.util.Collection; import java.util.*;
import java.util.HashSet; import java.util.concurrent.BlockingQueue;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class JailingQueue<T> extends PriorityBlockingQueue<T> { public class JailingQueue<T> extends AbstractQueue<T> implements BlockingQueue<T> {
private final PriorityBlockingQueue<T> delegate; private final BlockingQueue<T> delegate;
private final Set<Thread> jailed = new HashSet<Thread>(); private final Set<Thread> jailed = new HashSet<Thread>();
public JailingQueue(PriorityBlockingQueue<T> delegate) { public JailingQueue(BlockingQueue<T> delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
@@ -62,13 +59,13 @@ public class JailingQueue<T> extends PriorityBlockingQueue<T> {
} }
@Override @Override
public void put(T t) { public void put(T t) throws InterruptedException {
tryAccess(); tryAccess();
delegate.put(t); delegate.put(t);
} }
@Override @Override
public boolean offer(T t, long timeout, TimeUnit unit) { public boolean offer(T t, long timeout, TimeUnit unit) throws InterruptedException {
tryAccess(); tryAccess();
return delegate.offer(t, timeout, unit); return delegate.offer(t, timeout, unit);
} }

View File

@@ -143,7 +143,6 @@ public class InputReplayTimer extends WrappedTimer {
// Following are a ton of vanilla keyboard shortcuts, some are removed as they're useless in the // Following are a ton of vanilla keyboard shortcuts, some are removed as they're useless in the
// replay viewer as of now // replay viewer as of now
// TODO Update maybe add new key bindings
// TODO: Translate magic values to Keyboard.KEY_ constants // TODO: Translate magic values to Keyboard.KEY_ constants
if (key == 32 && Keyboard.isKeyDown(61) && mc.ingameGUI != null) { if (key == 32 && Keyboard.isKeyDown(61) && mc.ingameGUI != null) {

View File

@@ -23,8 +23,8 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.EnumPacketDirection; import net.minecraft.network.EnumPacketDirection;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher; import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
@@ -76,7 +76,7 @@ public class ReplayHandler {
Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread."); Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread.");
this.replayFile = replayFile; this.replayFile = replayFile;
MinecraftForge.EVENT_BUS.post(new ReplayOpenEvent.Pre(this)); FMLCommonHandler.instance().bus().post(new ReplayOpenEvent.Pre(this));
markers = new ArrayList<>(replayFile.getMarkers().or(Collections.emptySet())); markers = new ArrayList<>(replayFile.getMarkers().or(Collections.emptySet()));
@@ -87,7 +87,7 @@ public class ReplayHandler {
overlay = new GuiReplayOverlay(this); overlay = new GuiReplayOverlay(this);
overlay.setVisible(true); overlay.setVisible(true);
MinecraftForge.EVENT_BUS.post(new ReplayOpenEvent.Post(this)); FMLCommonHandler.instance().bus().post(new ReplayOpenEvent.Post(this));
} }
void restartedReplay() { void restartedReplay() {
@@ -101,7 +101,7 @@ public class ReplayHandler {
public void endReplay() throws IOException { public void endReplay() throws IOException {
Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread."); Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread.");
MinecraftForge.EVENT_BUS.post(new ReplayCloseEvent.Pre(this)); FMLCommonHandler.instance().bus().post(new ReplayCloseEvent.Pre(this));
replaySender.terminateReplay(); replaySender.terminateReplay();
@@ -124,7 +124,7 @@ public class ReplayHandler {
ReplayModReplay.instance.replayHandler = null; ReplayModReplay.instance.replayHandler = null;
MinecraftForge.EVENT_BUS.post(new ReplayCloseEvent.Post(this)); FMLCommonHandler.instance().bus().post(new ReplayCloseEvent.Post(this));
} }
private void setup() { private void setup() {
@@ -209,6 +209,9 @@ public class ReplayHandler {
*/ */
public void spectateEntity(Entity e) { public void spectateEntity(Entity e) {
CameraEntity cameraEntity = getCameraEntity(); CameraEntity cameraEntity = getCameraEntity();
if (cameraEntity == null) {
return; // Cannot spectate if we have no camera
}
if (e == null || e == cameraEntity) { if (e == null || e == cameraEntity) {
spectating = null; spectating = null;
e = cameraEntity; e = cameraEntity;
@@ -328,7 +331,7 @@ public class ReplayHandler {
replaySender.setAsyncMode(true); replaySender.setAsyncMode(true);
replaySender.setReplaySpeed(0); replaySender.setReplaySpeed(0);
mc.getConnection().getNetworkManager().processReceivedPackets(); mc.getNetHandler().getNetworkManager().processReceivedPackets();
for (Entity entity : mc.theWorld.loadedEntityList) { for (Entity entity : mc.theWorld.loadedEntityList) {
if (entity instanceof EntityOtherPlayerMP) { if (entity instanceof EntityOtherPlayerMP) {
EntityOtherPlayerMP e = (EntityOtherPlayerMP) entity; EntityOtherPlayerMP e = (EntityOtherPlayerMP) entity;

View File

@@ -6,7 +6,11 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.ModCompat; import com.replaymod.core.utils.ModCompat;
import com.replaymod.replay.camera.*; import com.replaymod.replay.camera.CameraController;
import com.replaymod.replay.camera.CameraControllerRegistry;
import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.camera.ClassicCameraController;
import com.replaymod.replay.camera.VanillaCameraController;
import com.replaymod.replay.gui.overlay.GuiMarkerTimeline; import com.replaymod.replay.gui.overlay.GuiMarkerTimeline;
import com.replaymod.replay.gui.screen.GuiModCompatWarning; import com.replaymod.replay.gui.screen.GuiModCompatWarning;
import com.replaymod.replay.handler.GuiHandler; import com.replaymod.replay.handler.GuiHandler;
@@ -99,6 +103,7 @@ public class ReplayModReplay {
@Override @Override
public void onSuccess(NoGuiScreenshot result) { public void onSuccess(NoGuiScreenshot result) {
try { try {
core.printInfoToChat("replaymod.chat.savingthumb");
replayHandler.getReplayFile().writeThumb(result.getImage()); replayHandler.getReplayFile().writeThumb(result.getImage());
core.printInfoToChat("replaymod.chat.savedthumb"); core.printInfoToChat("replaymod.chat.savedthumb");
} catch (IOException e) { } catch (IOException e) {
@@ -109,6 +114,7 @@ public class ReplayModReplay {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
t.printStackTrace(); t.printStackTrace();
core.printWarningToChat("replaymod.chat.failedthumb");
} }
}); });
} }

View File

@@ -12,22 +12,17 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityOtherPlayerMP;
import net.minecraft.client.gui.GuiDownloadTerrain; import net.minecraft.client.gui.GuiDownloadTerrain;
import net.minecraft.client.gui.GuiErrorScreen; import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.*; import net.minecraft.network.*;
import net.minecraft.network.play.server.*; import net.minecraft.network.play.server.*;
import net.minecraft.util.text.ITextComponent; import net.minecraft.util.IChatComponent;
import net.minecraft.world.EnumDifficulty; import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.GameType;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldSettings.GameType;
import net.minecraft.world.WorldType; import net.minecraft.world.WorldType;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@@ -44,22 +39,30 @@ import java.util.concurrent.Callable;
*/ */
@Sharable @Sharable
public class ReplaySender extends ChannelInboundHandlerAdapter { public class ReplaySender extends ChannelInboundHandlerAdapter {
/**
* Previously packets for the client player were inserted using one fixed entity id (this one).
* This is no longer the case however to provide backwards compatibility, we have to convert
* these old packets to use the normal entity id.
* Need to punch someone? -> CrushedPixel
*/
public static final int LEGACY_ENTITY_ID = Integer.MIN_VALUE + 9001;
/** /**
* These packets are ignored completely during replay. * These packets are ignored completely during replay.
*/ */
private static final List<Class> BAD_PACKETS = Arrays.<Class>asList( private static final List<Class> BAD_PACKETS = Arrays.<Class>asList(
// TODO Update possibly more? S06PacketUpdateHealth.class,
SPacketUpdateHealth.class, S2DPacketOpenWindow.class,
SPacketOpenWindow.class, S2EPacketCloseWindow.class,
SPacketCloseWindow.class, S2FPacketSetSlot.class,
SPacketSetSlot.class, S30PacketWindowItems.class,
SPacketWindowItems.class, S36PacketSignEditorOpen.class,
SPacketSignEditorOpen.class, S37PacketStatistics.class,
SPacketStatistics.class, S1FPacketSetExperience.class,
SPacketSetExperience.class, S43PacketCamera.class,
SPacketCamera.class, S39PacketPlayerAbilities.class,
SPacketPlayerAbilities.class, S45PacketTitle.class);
SPacketTitle.class);
private static int TP_DISTANCE_LIMIT = 128; private static int TP_DISTANCE_LIMIT = 128;
@@ -168,8 +171,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
this.asyncMode = asyncMode; this.asyncMode = asyncMode;
this.replayLength = file.getMetaData().getDuration(); this.replayLength = file.getMetaData().getDuration();
MinecraftForge.EVENT_BUS.register(this);
if (asyncMode) { if (asyncMode) {
new Thread(asyncSender, "replaymod-async-sender").start(); new Thread(asyncSender, "replaymod-async-sender").start();
} }
@@ -238,7 +239,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
*/ */
public void terminateReplay() { public void terminateReplay() {
terminate = true; terminate = true;
MinecraftForge.EVENT_BUS.unregister(this);
try { try {
channelInactive(ctx); channelInactive(ctx);
ctx.channel().pipeline().close(); ctx.channel().pipeline().close();
@@ -247,25 +247,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} }
} }
@SubscribeEvent
public void onWorldTick(TickEvent.ClientTickEvent event) {
// Unfortunately the WorldTickEvent doesn't seem to be emitted on the CLIENT side
if (event.phase != TickEvent.Phase.START) return;
// Spawning a player into an empty chunk (which we might do with the recording player)
// prevents it from being moved by teleport packets (it essentially gets stuck) because
// Entity#addedToChunk is not set and it is therefore not updated every tick.
// To counteract this, we need to manually update it's position if it hasn't been added
// to any chunk yet.
if (mc.theWorld != null) {
for (EntityPlayer playerEntity : mc.theWorld.playerEntities) {
if (!playerEntity.addedToChunk && playerEntity instanceof EntityOtherPlayerMP) {
playerEntity.onLivingUpdate();
}
}
}
}
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception { throws Exception {
@@ -293,21 +274,21 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
// Therefore we have to remove all loaded, dead entities manually if we are in sync mode. // 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. // We do this after every SpawnX packet and after the destroy entities packet.
if (!asyncMode && mc.theWorld != null) { if (!asyncMode && mc.theWorld != null) {
if (p instanceof SPacketSpawnPlayer if (p instanceof S0CPacketSpawnPlayer
|| p instanceof SPacketSpawnObject || p instanceof S0EPacketSpawnObject
|| p instanceof SPacketSpawnMob || p instanceof S0FPacketSpawnMob
|| p instanceof SPacketSpawnGlobalEntity || p instanceof S2CPacketSpawnGlobalEntity
|| p instanceof SPacketSpawnPainting || p instanceof S10PacketSpawnPainting
|| p instanceof SPacketSpawnExperienceOrb || p instanceof S11PacketSpawnExperienceOrb
|| p instanceof SPacketDestroyEntities) { || p instanceof S13PacketDestroyEntities) {
World world = mc.theWorld; World world = mc.theWorld;
for (int i = 0; i < world.loadedEntityList.size(); ++i) { for (int i = 0; i < world.loadedEntityList.size(); ++i) {
Entity entity = world.loadedEntityList.get(i); Entity entity = (Entity) world.loadedEntityList.get(i);
if (entity.isDead) { if (entity.isDead) {
int chunkX = entity.chunkCoordX; int chunkX = entity.chunkCoordX;
int chunkY = entity.chunkCoordZ; int chunkY = entity.chunkCoordZ;
if (entity.addedToChunk && world.getChunkProvider().getLoadedChunk(chunkX, chunkY) != null) { if (entity.addedToChunk && world.getChunkProvider().chunkExists(chunkX, chunkY)) {
world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity); world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity);
} }
@@ -345,8 +326,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
* @return The processed packet or {@code null} if no packet shall be sent * @return The processed packet or {@code null} if no packet shall be sent
*/ */
protected Packet processPacket(Packet p) throws Exception { protected Packet processPacket(Packet p) throws Exception {
if (p instanceof SPacketCustomPayload) { if (p instanceof S3FPacketCustomPayload) {
SPacketCustomPayload packet = (SPacketCustomPayload) p; S3FPacketCustomPayload packet = (S3FPacketCustomPayload) p;
if (Restrictions.PLUGIN_CHANNEL.equals(packet.getChannelName())) { if (Restrictions.PLUGIN_CHANNEL.equals(packet.getChannelName())) {
final String unknown = replayHandler.getRestrictions().handle(packet); final String unknown = replayHandler.getRestrictions().handle(packet);
if (unknown == null) { if (unknown == null) {
@@ -372,8 +353,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} }
} }
} }
if (p instanceof SPacketDisconnect) { if (p instanceof S40PacketDisconnect) {
ITextComponent reason = ((SPacketDisconnect) p).getReason(); IChatComponent reason = ((S40PacketDisconnect) p).getReason();
if ("Please update to view this replay.".equals(reason.getUnformattedText())) { if ("Please update to view this replay.".equals(reason.getUnformattedText())) {
// This version of the mod supports replay restrictions so we are allowed // This version of the mod supports replay restrictions so we are allowed
// to remove this packet. // to remove this packet.
@@ -383,8 +364,10 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
if(BAD_PACKETS.contains(p.getClass())) return null; if(BAD_PACKETS.contains(p.getClass())) return null;
if(p instanceof SPacketResourcePackSend) { convertLegacyEntityIds(p);
SPacketResourcePackSend packet = (SPacketResourcePackSend) p;
if(p instanceof S48PacketResourcePackSend) {
S48PacketResourcePackSend packet = (S48PacketResourcePackSend) p;
String url = packet.getURL(); String url = packet.getURL();
if (url.startsWith("replay://")) { if (url.startsWith("replay://")) {
int id = Integer.parseInt(url.substring("replay://".length())); int id = Integer.parseInt(url.substring("replay://".length()));
@@ -403,10 +386,10 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} }
} }
if(p instanceof SPacketJoinGame) { if(p instanceof S01PacketJoinGame) {
SPacketJoinGame packet = (SPacketJoinGame) p; S01PacketJoinGame packet = (S01PacketJoinGame) p;
allowMovement = true; allowMovement = true;
int entId = packet.getPlayerId(); int entId = packet.getEntityId();
actualID = entId; actualID = entId;
entId = -1789435; // Camera entity id should be negative which is an invalid id and can't be used by servers entId = -1789435; // Camera entity id should be negative which is an invalid id and can't be used by servers
int dimension = packet.getDimension(); int dimension = packet.getDimension();
@@ -414,21 +397,21 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
int maxPlayers = packet.getMaxPlayers(); int maxPlayers = packet.getMaxPlayers();
WorldType worldType = packet.getWorldType(); WorldType worldType = packet.getWorldType();
p = new SPacketJoinGame(entId, GameType.SPECTATOR, false, dimension, p = new S01PacketJoinGame(entId, GameType.SPECTATOR, false, dimension,
difficulty, maxPlayers, worldType, false); difficulty, maxPlayers, worldType, false);
} }
if(p instanceof SPacketRespawn) { if(p instanceof S07PacketRespawn) {
SPacketRespawn respawn = (SPacketRespawn) p; S07PacketRespawn respawn = (S07PacketRespawn) p;
p = new SPacketRespawn(respawn.getDimensionID(), p = new S07PacketRespawn(respawn.getDimensionID(),
respawn.getDifficulty(), respawn.getWorldType(), GameType.SPECTATOR); respawn.getDifficulty(), respawn.getWorldType(), GameType.SPECTATOR);
allowMovement = true; allowMovement = true;
} }
if(p instanceof SPacketPlayerPosLook) { if(p instanceof S08PacketPlayerPosLook) {
if(!hasWorldLoaded) hasWorldLoaded = true; if(!hasWorldLoaded) hasWorldLoaded = true;
final SPacketPlayerPosLook ppl = (SPacketPlayerPosLook) p; final S08PacketPlayerPosLook ppl = (S08PacketPlayerPosLook) p;
if (mc.currentScreen instanceof GuiDownloadTerrain) { if (mc.currentScreen instanceof GuiDownloadTerrain) {
// Close the world loading screen manually in case we swallow the packet // Close the world loading screen manually in case we swallow the packet
@@ -439,10 +422,10 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
CameraEntity cent = replayHandler.getCameraEntity(); CameraEntity cent = replayHandler.getCameraEntity();
for (SPacketPlayerPosLook.EnumFlags relative : ppl.getFlags()) { for (Object relative : ppl.func_179834_f()) {
if (relative == SPacketPlayerPosLook.EnumFlags.X if (relative == S08PacketPlayerPosLook.EnumFlags.X
|| relative == SPacketPlayerPosLook.EnumFlags.Y || relative == S08PacketPlayerPosLook.EnumFlags.Y
|| relative == SPacketPlayerPosLook.EnumFlags.Z) { || relative == S08PacketPlayerPosLook.EnumFlags.Z) {
return null; // At least one of the coordinates is relative, so we don't care return null; // At least one of the coordinates is relative, so we don't care
} }
} }
@@ -474,8 +457,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
}.call(); }.call();
} }
if(p instanceof SPacketChangeGameState) { if(p instanceof S2BPacketChangeGameState) {
SPacketChangeGameState pg = (SPacketChangeGameState)p; S2BPacketChangeGameState pg = (S2BPacketChangeGameState)p;
int reason = pg.getGameState(); int reason = pg.getGameState();
// only allow the following packets: // only allow the following packets:
@@ -490,7 +473,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} }
} }
if (p instanceof SPacketChat) { if (p instanceof S02PacketChat) {
if (!ReplayModReplay.instance.getCore().getSettingsRegistry().get(Setting.SHOW_CHAT)) { if (!ReplayModReplay.instance.getCore().getSettingsRegistry().get(Setting.SHOW_CHAT)) {
return null; return null;
} }
@@ -503,7 +486,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void channelActive(ChannelHandlerContext ctx) throws Exception { public void channelActive(ChannelHandlerContext ctx) throws Exception {
this.ctx = ctx; this.ctx = ctx;
ctx.attr(NetworkManager.PROTOCOL_ATTRIBUTE_KEY).set(EnumConnectionState.PLAY); ctx.attr(NetworkManager.attrKeyConnectionState).set(EnumConnectionState.PLAY);
super.channelActive(ctx); super.channelActive(ctx);
} }
@@ -708,10 +691,10 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
protected Packet processPacketAsync(Packet p) { protected Packet processPacketAsync(Packet p) {
//If hurrying, ignore some packets, except for short durations //If hurrying, ignore some packets, except for short durations
if(desiredTimeStamp - lastTimeStamp > 1000) { if(desiredTimeStamp - lastTimeStamp > 1000) {
if(p instanceof SPacketParticles) return null; if(p instanceof S2APacketParticles) return null;
if(p instanceof SPacketSpawnObject) { if(p instanceof S0EPacketSpawnObject) {
SPacketSpawnObject pso = (SPacketSpawnObject)p; S0EPacketSpawnObject pso = (S0EPacketSpawnObject)p;
int type = pso.getType(); int type = pso.getType();
if(type == 76) { // Firework rocket if(type == 76) { // Firework rocket
return null; return null;
@@ -798,6 +781,68 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
return p; // During synchronous playback everything is sent normally return p; // During synchronous playback everything is sent normally
} }
/**
* This is necessary to convert packets from old replays to new replays.
* @param packet The packet to be transformed.
* @see #LEGACY_ENTITY_ID
*/
private void convertLegacyEntityIds(Packet packet) {
if (packet instanceof S0CPacketSpawnPlayer) {
S0CPacketSpawnPlayer p = (S0CPacketSpawnPlayer) packet;
if (p.entityId == LEGACY_ENTITY_ID) {
p.entityId = actualID;
}
} else if (packet instanceof S18PacketEntityTeleport) {
S18PacketEntityTeleport p = (S18PacketEntityTeleport) packet;
if (p.entityId == LEGACY_ENTITY_ID) {
p.entityId = actualID;
}
} else if (packet instanceof S14PacketEntity.S17PacketEntityLookMove) {
S14PacketEntity.S17PacketEntityLookMove p = (S14PacketEntity.S17PacketEntityLookMove) packet;
if (p.entityId == LEGACY_ENTITY_ID) {
p.entityId = actualID;
}
} else if (packet instanceof S19PacketEntityHeadLook) {
S19PacketEntityHeadLook p = (S19PacketEntityHeadLook) packet;
if (p.entityId == LEGACY_ENTITY_ID) {
p.entityId = actualID;
}
} else if (packet instanceof S12PacketEntityVelocity) {
S12PacketEntityVelocity p = (S12PacketEntityVelocity) packet;
if (p.entityID == LEGACY_ENTITY_ID) {
p.entityID = actualID;
}
} else if (packet instanceof S0BPacketAnimation) {
S0BPacketAnimation p = (S0BPacketAnimation) packet;
if (p.entityId == LEGACY_ENTITY_ID) {
p.entityId = actualID;
}
} else if (packet instanceof S04PacketEntityEquipment) {
S04PacketEntityEquipment p = (S04PacketEntityEquipment) packet;
if (p.entityID == LEGACY_ENTITY_ID) {
p.entityID = actualID;
}
} else if (packet instanceof S1BPacketEntityAttach) {
S1BPacketEntityAttach p = (S1BPacketEntityAttach) packet;
if (p.entityId == LEGACY_ENTITY_ID) {
p.entityId = actualID;
}
if (p.vehicleEntityId == LEGACY_ENTITY_ID) {
p.vehicleEntityId = actualID;
}
} else if (packet instanceof S0DPacketCollectItem) {
S0DPacketCollectItem p = (S0DPacketCollectItem) packet;
if (p.entityId == LEGACY_ENTITY_ID) {
p.entityId = actualID;
}
} else if (packet instanceof S13PacketDestroyEntities) {
S13PacketDestroyEntities p = (S13PacketDestroyEntities) packet;
if (p.entityIDs.length == 1 && p.entityIDs[0] == LEGACY_ENTITY_ID) {
p.entityIDs[0] = actualID;
}
}
}
private static final class PacketData { private static final class PacketData {
private final int timestamp; private final int timestamp;
private final byte[] bytes; private final byte[] bytes;

View File

@@ -18,17 +18,16 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.item.EntityItemFrame; import net.minecraft.entity.item.EntityItemFrame;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.stats.StatFileWriter;
import net.minecraft.stats.StatisticsManager; import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.EnumHand; import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.client.event.EntityViewRenderEvent; import net.minecraftforge.client.event.EntityViewRenderEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderHandEvent; import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -66,8 +65,9 @@ public class CameraEntity extends EntityPlayerSP {
*/ */
private final EventHandler eventHandler = new EventHandler(); private final EventHandler eventHandler = new EventHandler();
public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatisticsManager statisticsManager) { public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatFileWriter statFileWriter) {
super(mcIn, worldIn, netHandlerPlayClient, statisticsManager); super(mcIn, worldIn, netHandlerPlayClient, statFileWriter);
FMLCommonHandler.instance().bus().register(eventHandler);
MinecraftForge.EVENT_BUS.register(eventHandler); MinecraftForge.EVENT_BUS.register(eventHandler);
if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) { if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) {
cameraController = ReplayModReplay.instance.createCameraController(this); cameraController = ReplayModReplay.instance.createCameraController(this);
@@ -289,49 +289,12 @@ public class CameraEntity extends EntityPlayerSP {
} }
@Override @Override
public float getCooldownPeriod() { public MovingObjectPosition rayTrace(double p_174822_1_, float p_174822_3_) {
Entity view = mc.getRenderViewEntity(); MovingObjectPosition pos = super.rayTrace(p_174822_1_, 1f);
if (view != this && view instanceof EntityPlayer) {
return ((EntityPlayer) view).getCooldownPeriod();
}
return 1;
}
@Override
public float getCooledAttackStrength(float adjustTicks) {
Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof EntityPlayer) {
return ((EntityPlayer) view).getCooledAttackStrength(adjustTicks);
}
// Default to 1 as to not render the cooldown indicator (renders for < 1)
return 1;
}
@Override
public EnumHand getActiveHand() {
Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof EntityPlayer) {
return ((EntityPlayer) view).getActiveHand();
}
return super.getActiveHand();
}
@Override
public boolean isHandActive() {
Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof EntityPlayer) {
return ((EntityPlayer) view).isHandActive();
}
return super.isHandActive();
}
@Override
public RayTraceResult rayTrace(double p_174822_1_, float p_174822_3_) {
RayTraceResult pos = super.rayTrace(p_174822_1_, 1f);
// Make sure we can never look at blocks (-> no outline) // Make sure we can never look at blocks (-> no outline)
if(pos != null && pos.typeOfHit == RayTraceResult.Type.BLOCK) { if(pos != null && pos.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) {
pos.typeOfHit = RayTraceResult.Type.MISS; pos.typeOfHit = MovingObjectPosition.MovingObjectType.MISS;
} }
return pos; return pos;
@@ -346,6 +309,7 @@ public class CameraEntity extends EntityPlayerSP {
@Override @Override
public void setDead() { public void setDead() {
super.setDead(); super.setDead();
FMLCommonHandler.instance().bus().unregister(eventHandler);
MinecraftForge.EVENT_BUS.unregister(eventHandler); MinecraftForge.EVENT_BUS.unregister(eventHandler);
} }
@@ -403,13 +367,9 @@ public class CameraEntity extends EntityPlayerSP {
@SubscribeEvent @SubscribeEvent
public void preCrosshairRender(RenderGameOverlayEvent.Pre event) { public void preCrosshairRender(RenderGameOverlayEvent.Pre event) {
// The crosshair should only render if targeted entity can actually be spectated // The crosshair should only render if targeted entity can actually be spectated
if (event.getType() == RenderGameOverlayEvent.ElementType.CROSSHAIRS) { if (event.type == RenderGameOverlayEvent.ElementType.CROSSHAIRS) {
event.setCanceled(!canSpectate(mc.pointedEntity)); event.setCanceled(!canSpectate(mc.pointedEntity));
} }
// Hotbar should never be rendered
if (event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR) {
event.setCanceled(true);
}
} }
@SubscribeEvent @SubscribeEvent
@@ -440,13 +400,10 @@ public class CameraEntity extends EntityPlayerSP {
if (lastHandRendered != player) { if (lastHandRendered != player) {
lastHandRendered = player; lastHandRendered = player;
mc.entityRenderer.itemRenderer.prevEquippedProgressMainHand = 1; mc.entityRenderer.itemRenderer.prevEquippedProgress = 1;
mc.entityRenderer.itemRenderer.prevEquippedProgressOffHand = 1; mc.entityRenderer.itemRenderer.equippedProgress = 1;
mc.entityRenderer.itemRenderer.equippedProgressMainHand = 1; mc.entityRenderer.itemRenderer.itemToRender = player.inventory.getCurrentItem();
mc.entityRenderer.itemRenderer.equippedProgressOffHand = 1; mc.entityRenderer.itemRenderer.equippedItemSlot = player.inventory.currentItem;
mc.entityRenderer.itemRenderer.itemStackMainHand = player.getItemStackFromSlot(EntityEquipmentSlot.MAINHAND);
mc.entityRenderer.itemRenderer.itemStackOffHand = player.getItemStackFromSlot(EntityEquipmentSlot.OFFHAND);
mc.thePlayer.renderArmYaw = mc.thePlayer.prevRenderArmYaw = player.rotationYaw; mc.thePlayer.renderArmYaw = mc.thePlayer.prevRenderArmYaw = player.rotationYaw;
mc.thePlayer.renderArmPitch = mc.thePlayer.prevRenderArmPitch = player.rotationPitch; mc.thePlayer.renderArmPitch = mc.thePlayer.prevRenderArmPitch = player.rotationPitch;
@@ -457,7 +414,7 @@ public class CameraEntity extends EntityPlayerSP {
@SubscribeEvent @SubscribeEvent
public void onEntityViewRenderEvent(EntityViewRenderEvent.CameraSetup event) { public void onEntityViewRenderEvent(EntityViewRenderEvent.CameraSetup event) {
if (mc.getRenderViewEntity() == CameraEntity.this) { if (mc.getRenderViewEntity() == CameraEntity.this) {
event.setRoll(roll); event.roll = roll;
} }
} }
} }

View File

@@ -2,8 +2,8 @@ package com.replaymod.replay.camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding; import net.minecraft.client.settings.KeyBinding;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.Vec3;
import org.lwjgl.Sys; import org.lwjgl.Sys;
// TODO: Marius is responsible for this. Please, someone clean it up. // TODO: Marius is responsible for this. Please, someone clean it up.
@@ -18,8 +18,8 @@ public class ClassicCameraController implements CameraController {
private double THRESHOLD = MAX_SPEED / 20; private double THRESHOLD = MAX_SPEED / 20;
private double DECAY = MAX_SPEED/3; private double DECAY = MAX_SPEED/3;
private Vec3d direction; private Vec3 direction;
private Vec3d dirBefore; private Vec3 dirBefore;
private double motion; private double motion;
private long lastCall = Sys.getTime(); private long lastCall = Sys.getTime();
@@ -129,7 +129,7 @@ public class ClassicCameraController implements CameraController {
return; return;
} }
Vec3d movement = direction.normalize(); Vec3 movement = direction.normalize();
double factor = motion * (frac / 1000D); double factor = motion * (frac / 1000D);
camera.moveCamera(movement.xCoord * factor, movement.yCoord * factor, movement.zCoord * factor); camera.moveCamera(movement.xCoord * factor, movement.yCoord * factor, movement.zCoord * factor);
@@ -158,7 +158,7 @@ public class ClassicCameraController implements CameraController {
break; break;
} }
Vec3d dbf = direction; Vec3 dbf = direction;
if(dirBefore != null) { if(dirBefore != null) {
direction = dirBefore.normalize().add(direction); direction = dirBefore.normalize().add(direction);
@@ -169,12 +169,12 @@ public class ClassicCameraController implements CameraController {
updateMovement(); updateMovement();
} }
private Vec3d getVectorForRotation(float pitch, float yaw) { private Vec3 getVectorForRotation(float pitch, float yaw) {
float f2 = MathHelper.cos(-yaw * 0.017453292F - (float) Math.PI); float f2 = MathHelper.cos(-yaw * 0.017453292F - (float) Math.PI);
float f3 = MathHelper.sin(-yaw * 0.017453292F - (float)Math.PI); float f3 = MathHelper.sin(-yaw * 0.017453292F - (float)Math.PI);
float f4 = -MathHelper.cos(-pitch * 0.017453292F); float f4 = -MathHelper.cos(-pitch * 0.017453292F);
float f5 = MathHelper.sin(-pitch * 0.017453292F); float f5 = MathHelper.sin(-pitch * 0.017453292F);
return new Vec3d((double)(f3 * f4), (double)f5, (double)(f2 * f4)); return new Vec3((double)(f3 * f4), (double)f5, (double)(f2 * f4));
} }
public enum MoveDirection { public enum MoveDirection {

View File

@@ -41,9 +41,8 @@ public class SpectatorCameraController implements CameraController {
if (view instanceof EntityPlayer) { if (view instanceof EntityPlayer) {
EntityPlayer viewPlayer = (EntityPlayer) view; EntityPlayer viewPlayer = (EntityPlayer) view;
camera.inventory = viewPlayer.inventory; camera.inventory = viewPlayer.inventory;
camera.itemStackMainHand = viewPlayer.itemStackMainHand; camera.itemInUse = viewPlayer.itemInUse;
camera.swingingHand = viewPlayer.swingingHand; camera.itemInUseCount = viewPlayer.itemInUseCount;
camera.activeItemStackUseCount = viewPlayer.activeItemStackUseCount;
} }
} }
} }

View File

@@ -2,14 +2,14 @@ package com.replaymod.replay.gui.overlay;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.util.Location; import com.replaymod.replaystudio.util.Location;
import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline; import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline;
import de.johni0702.minecraft.gui.function.Draggable; import de.johni0702.minecraft.gui.function.Draggable;
import com.replaymod.replaystudio.data.Marker;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.MathHelper;
import org.lwjgl.util.Point; import org.lwjgl.util.Point;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
import org.lwjgl.util.ReadablePoint; import org.lwjgl.util.ReadablePoint;

View File

@@ -16,7 +16,7 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.client.settings.GameSettings; import net.minecraft.client.settings.GameSettings;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.InputEvent; import net.minecraftforge.fml.common.gameevent.InputEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
@@ -138,9 +138,9 @@ public class GuiReplayOverlay extends AbstractGuiOverlay<GuiReplayOverlay> {
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
if (isVisible() != visible) { if (isVisible() != visible) {
if (visible) { if (visible) {
MinecraftForge.EVENT_BUS.register(this); FMLCommonHandler.instance().bus().register(this);
} else { } else {
MinecraftForge.EVENT_BUS.unregister(this); FMLCommonHandler.instance().bus().unregister(this);
} }
} }
super.setVisible(visible); super.setVisible(visible);

View File

@@ -9,14 +9,15 @@ import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
public class GuiHandler { public class GuiHandler {
private static final int BUTTON_EXIT_SERVER = 1; private static final int BUTTON_EXIT_SERVER = 1;
private static final int BUTTON_RETURN_TO_GAME = 4;
private static final int BUTTON_ACHIEVEMENTS = 5; private static final int BUTTON_ACHIEVEMENTS = 5;
private static final int BUTTON_STATS = 6; private static final int BUTTON_STATS = 6;
private static final int BUTTON_OPEN_TO_LAN = 7; private static final int BUTTON_OPEN_TO_LAN = 7;
@@ -33,12 +34,13 @@ public class GuiHandler {
} }
public void register() { public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent
public void injectIntoIngameMenu(GuiScreenEvent.InitGuiEvent.Post event) { public void injectIntoIngameMenu(GuiScreenEvent.InitGuiEvent.Post event) {
if (!(event.getGui() instanceof GuiIngameMenu)) { if (!(event.gui instanceof GuiIngameMenu)) {
return; return;
} }
@@ -46,7 +48,10 @@ public class GuiHandler {
// Pause replay when menu is opened // Pause replay when menu is opened
mod.getReplayHandler().getReplaySender().setReplaySpeed(0); mod.getReplayHandler().getReplaySender().setReplaySpeed(0);
for(GuiButton b : new ArrayList<>(event.getButtonList())) { GuiButton achievements = null, stats = null, openToLan = null;
@SuppressWarnings("unchecked")
List<GuiButton> buttonList = event.buttonList;
for(GuiButton b : new ArrayList<>(buttonList)) {
switch (b.id) { switch (b.id) {
// Replace "Exit Server" button with "Exit Replay" button // Replace "Exit Server" button with "Exit Replay" button
case BUTTON_EXIT_SERVER: case BUTTON_EXIT_SERVER:
@@ -55,43 +60,69 @@ public class GuiHandler {
break; break;
// Remove "Achievements", "Stats" and "Open to LAN" buttons // Remove "Achievements", "Stats" and "Open to LAN" buttons
case BUTTON_ACHIEVEMENTS: case BUTTON_ACHIEVEMENTS:
buttonList.remove(achievements = b);
break;
case BUTTON_STATS: case BUTTON_STATS:
buttonList.remove(stats = b);
break;
case BUTTON_OPEN_TO_LAN: case BUTTON_OPEN_TO_LAN:
event.getButtonList().remove(b); buttonList.remove(openToLan = b);
} break;
// Move all buttons except the "Return to game" button upwards
if (b.id != BUTTON_RETURN_TO_GAME) {
b.yPosition -= 48;
} }
} }
if (achievements != null && stats != null) {
moveAllButtonsDirectlyBelowUpwards(buttonList, achievements.yPosition,
achievements.xPosition, stats.xPosition + stats.width);
}
if (openToLan != null) {
moveAllButtonsDirectlyBelowUpwards(buttonList, openToLan.yPosition,
openToLan.xPosition, openToLan.xPosition + openToLan.width);
}
}
}
/**
* Moves all buttons that are within a rectangle below a certain y coordinate upwards by 24 units.
* @param buttons List of buttons
* @param belowY The Y limit
* @param xStart Left x limit of the rectangle
* @param xEnd Right x limit of the rectangle
*/
private void moveAllButtonsDirectlyBelowUpwards(List<GuiButton> buttons, int belowY, int xStart, int xEnd) {
for (GuiButton button : buttons) {
if (button.yPosition >= belowY && button.xPosition <= xEnd && button.xPosition + button.width >= xStart) {
button.yPosition -= 24;
}
} }
} }
@SubscribeEvent @SubscribeEvent
public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) { public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) {
if (!(event.getGui() instanceof GuiMainMenu)) { if (!(event.gui instanceof GuiMainMenu)) {
return; return;
} }
GuiButton button = new GuiButton(BUTTON_REPLAY_VIEWER, event.getGui().width / 2 - 100, @SuppressWarnings("unchecked")
event.getGui().height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayviewer")); List<GuiButton> buttonList = event.buttonList;
GuiButton button = new GuiButton(BUTTON_REPLAY_VIEWER, event.gui.width / 2 - 100,
event.gui.height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayviewer"));
button.width = button.width / 2 - 2; button.width = button.width / 2 - 2;
event.getButtonList().add(button); buttonList.add(button);
} }
@SubscribeEvent @SubscribeEvent
public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) { public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) {
if(!event.getButton().enabled) return; if(!event.button.enabled) return;
if (event.getGui() instanceof GuiMainMenu) { if (event.gui instanceof GuiMainMenu) {
if (event.getButton().id == BUTTON_REPLAY_VIEWER) { if (event.button.id == BUTTON_REPLAY_VIEWER) {
new GuiReplayViewer(mod).display(); new GuiReplayViewer(mod).display();
} }
} }
if (event.getGui() instanceof GuiIngameMenu && mod.getReplayHandler() != null) { if (event.gui instanceof GuiIngameMenu && mod.getReplayHandler() != null) {
if (event.getButton().id == BUTTON_EXIT_REPLAY) { if (event.button.id == BUTTON_EXIT_REPLAY) {
event.getButton().enabled = false; event.button.enabled = false;
mc.displayGuiScreen(new GuiMainMenu()); mc.displayGuiScreen(new GuiMainMenu());
try { try {
mod.getReplayHandler().endReplay(); mod.getReplayHandler().endReplay();

View File

@@ -2,7 +2,7 @@ package com.replaymod.replay.mixin;
import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.RenderArmorStand; import net.minecraft.client.renderer.entity.ArmorStandRenderer;
import net.minecraft.entity.item.EntityArmorStand; import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -10,8 +10,8 @@ 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.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(RenderArmorStand.class) @Mixin(ArmorStandRenderer.class)
public abstract class MixinRenderArmorStand { public abstract class MixinArmorStandRenderer {
@Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true) @Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true)
private void replayModReplay_canRenderInvisibleName(EntityArmorStand entity, CallbackInfoReturnable<Boolean> ci) { private void replayModReplay_canRenderInvisibleName(EntityArmorStand entity, CallbackInfoReturnable<Boolean> ci) {
EntityPlayer thePlayer = Minecraft.getMinecraft().thePlayer; EntityPlayer thePlayer = Minecraft.getMinecraft().thePlayer;

View File

@@ -12,12 +12,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GuiSpectator.class) @Mixin(GuiSpectator.class)
public abstract class MixinGuiSpectator { public abstract class MixinGuiSpectator {
@Shadow @Shadow
private Minecraft mc; private Minecraft field_175268_g;
@Inject(method = "onHotbarSelected", at = @At("HEAD"), cancellable = true) @Inject(method = "func_175260_a", at = @At("HEAD"), cancellable = true)
public void isInReplay(int i, CallbackInfo ci) { public void isInReplay(int i, CallbackInfo ci) {
// Prevent spectator gui from opening while in a replay // Prevent spectator gui from opening while in a replay
if (mc.thePlayer instanceof CameraEntity) { if (field_175268_g.thePlayer instanceof CameraEntity) {
ci.cancel(); ci.cancel();
} }
} }

View File

@@ -1,31 +0,0 @@
package com.replaymod.replay.mixin;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.world.World;
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;
import java.util.Queue;
@Mixin(ParticleManager.class)
public abstract class MixinParticleManager {
@Shadow
private Queue<Particle> queueEntityFX;
/**
* This method additionally clears the queue of particles to be added when the world is changed.
* Otherwise particles from the previous world might show up in this one if they were spawned after
* the last tick in the previous world.
*
* @param world The new world
* @param ci Callback info
*/
@Inject(method = "clearEffects", at = @At("HEAD"))
public void replayModReplay_clearParticleQueue(World world, CallbackInfo ci) {
queueEntityFX.clear();
}
}

View File

@@ -1,12 +1,12 @@
package com.replaymod.replay.mixin; package com.replaymod.replay.mixin;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.multiplayer.PlayerControllerMP; import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.stats.StatisticsManager; import net.minecraft.stats.StatFileWriter;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -21,12 +21,12 @@ public abstract class MixinPlayerControllerMP {
private Minecraft mc; private Minecraft mc;
@Shadow @Shadow
private NetHandlerPlayClient connection; private NetHandlerPlayClient netClientHandler;
@Inject(method = "createClientPlayer", at=@At("HEAD"), cancellable = true) @Inject(method = "func_178892_a", at=@At("HEAD"), cancellable = true)
private void replayModReplay_createReplayCamera(World worldIn, StatisticsManager statisticsManager, CallbackInfoReturnable<EntityPlayerSP> ci) { private void replayModReplay_createReplayCamera(World worldIn, StatFileWriter statFileWriter, CallbackInfoReturnable<EntityPlayerSP> ci) {
if (ReplayModReplay.instance.getReplayHandler() != null) { if (ReplayModReplay.instance.getReplayHandler() != null) {
ci.setReturnValue(new CameraEntity(mc, worldIn, connection, statisticsManager)); ci.setReturnValue(new CameraEntity(mc, worldIn, netClientHandler, statFileWriter));
ci.cancel(); ci.cancel();
} }
} }

View File

@@ -14,7 +14,6 @@ public abstract class MixinRenderArrow extends Render {
super(renderManager); super(renderManager);
} }
@SuppressWarnings("unchecked")
@Override @Override
public boolean shouldRender(Entity entity, ICamera camera, double camX, double camY, double camZ) { public boolean shouldRender(Entity entity, ICamera camera, double camX, double camY, double camZ) {
// Force arrows to always render, otherwise they stop rendering when you get close to them // Force arrows to always render, otherwise they stop rendering when you get close to them

View File

@@ -3,7 +3,7 @@ package com.replaymod.replay.mixin;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderItem; import net.minecraft.client.renderer.entity.RenderItem;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;

View File

@@ -2,7 +2,7 @@ package com.replaymod.replay.mixin;
import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.RenderLivingBase; import net.minecraft.client.renderer.entity.RendererLivingEntity;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -11,8 +11,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(RenderLivingBase.class) @Mixin(RendererLivingEntity.class)
public abstract class MixinRenderLivingBase { public abstract class MixinRendererLivingEntity {
@Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true) @Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true)
private void replayModReplay_canRenderInvisibleName(EntityLivingBase entity, CallbackInfoReturnable<Boolean> ci) { private void replayModReplay_canRenderInvisibleName(EntityLivingBase entity, CallbackInfoReturnable<Boolean> ci) {
EntityPlayer thePlayer = Minecraft.getMinecraft().thePlayer; EntityPlayer thePlayer = Minecraft.getMinecraft().thePlayer;

View File

@@ -5,8 +5,8 @@ import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.ViewFrustum; import net.minecraft.client.renderer.ViewFrustum;
import net.minecraft.client.renderer.chunk.IRenderChunkFactory; import net.minecraft.client.renderer.chunk.IRenderChunkFactory;
import net.minecraft.client.renderer.chunk.RenderChunk; import net.minecraft.client.renderer.chunk.RenderChunk;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.MathHelper;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -31,7 +31,7 @@ public abstract class MixinViewFrustum {
} }
/** /**
* Instead of calling {@link RenderChunk#setOrigin(int, int, int)} we recreate the render chunk * Instead of calling {@link RenderChunk#setPosition(BlockPos)} we recreate the render chunk
* which seems to solve the problem that chunks are invisible when you leave an area and return * which seems to solve the problem that chunks are invisible when you leave an area and return
* to it. * to it.
* Any better fixes are welcome. * Any better fixes are welcome.
@@ -48,18 +48,17 @@ public abstract class MixinViewFrustum {
int k = this.countChunksX * 16; int k = this.countChunksX * 16;
for (int l = 0; l < this.countChunksX; ++l) { for (int l = 0; l < this.countChunksX; ++l) {
int i1 = this.getBaseCoordinate(i, k, l); int i1 = this.func_178157_a(i, k, l);
for (int j1 = 0; j1 < this.countChunksZ; ++j1) { for (int j1 = 0; j1 < this.countChunksZ; ++j1) {
int k1 = this.getBaseCoordinate(j, k, j1); int k1 = this.func_178157_a(j, k, j1);
for (int l1 = 0; l1 < this.countChunksY; ++l1) { for (int l1 = 0; l1 < this.countChunksY; ++l1) {
int i2 = l1 * 16; int i2 = l1 * 16;
RenderChunk renderchunk = this.renderChunks[(j1 * this.countChunksY + l1) * this.countChunksX + l]; RenderChunk renderchunk = this.renderChunks[(j1 * this.countChunksY + l1) * this.countChunksX + l];
BlockPos blockpos = new BlockPos(i1, i2, k1); BlockPos blockpos = new BlockPos(i1, i2, k1);
if (!blockpos.equals(renderchunk.getPosition())) { if (!blockpos.equals(renderchunk.getPosition())) {
// Recreate render chunk instead of setting its position // Recreate render chunk instead of setting its position
(renderChunks[(j1 * this.countChunksY + l1) * this.countChunksX + l] = renderChunks[(j1 * this.countChunksY + l1) * this.countChunksX + l] =
renderChunkFactory.create(world, renderGlobal, 0) renderChunkFactory.makeRenderChunk(world, renderGlobal, blockpos, 0);
).setOrigin(blockpos.getX(), blockpos.getY(), blockpos.getZ());
} }
} }
} }
@@ -67,5 +66,5 @@ public abstract class MixinViewFrustum {
ci.cancel(); ci.cancel();
} }
@Shadow abstract int getBaseCoordinate(int p_178157_1_, int p_178157_2_, int p_178157_3_); @Shadow abstract int func_178157_a(int p_178157_1_, int p_178157_2_, int p_178157_3_);
} }

View File

@@ -9,11 +9,12 @@ import com.replaymod.simplepathing.SPTimeline.SPPath;
import com.replaymod.simplepathing.gui.GuiPathing; import com.replaymod.simplepathing.gui.GuiPathing;
import com.replaymod.simplepathing.preview.PathPreview; import com.replaymod.simplepathing.preview.PathPreview;
import lombok.Getter; import lombok.Getter;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
@Mod(modid = ReplayModSimplePathing.MOD_ID, @Mod(modid = ReplayModSimplePathing.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
@@ -22,6 +23,9 @@ import org.apache.logging.log4j.Logger;
public class ReplayModSimplePathing { public class ReplayModSimplePathing {
public static final String MOD_ID = "replaymod-simplepathing"; public static final String MOD_ID = "replaymod-simplepathing";
@Mod.Instance(MOD_ID)
public static ReplayModSimplePathing instance;
private ReplayMod core; private ReplayMod core;
public static Logger LOGGER; public static Logger LOGGER;
@@ -35,15 +39,28 @@ public class ReplayModSimplePathing {
core.getSettingsRegistry().register(Setting.class); core.getSettingsRegistry().register(Setting.class);
MinecraftForge.EVENT_BUS.register(this); FMLCommonHandler.instance().bus().register(this);
PathPreview pathPreview = new PathPreview(this); PathPreview pathPreview = new PathPreview(this);
pathPreview.register(); pathPreview.register();
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.keyframerepository", Keyboard.KEY_X, () -> {
if (guiPathing != null) guiPathing.keyframeRepoButtonPressed();
});
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.clearkeyframes", Keyboard.KEY_C, () -> {
if (guiPathing != null) guiPathing.clearKeyframesButtonPressed();
});
core.getKeyBindingRegistry().registerRepeatedKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> {
if (guiPathing != null) guiPathing.syncTimeButtonPressed();
});
core.getKeyBindingRegistry().registerRaw(Keyboard.KEY_DELETE, () -> {
if (guiPathing != null) guiPathing.deleteButtonPressed();
});
} }
@SubscribeEvent @SubscribeEvent
public void postReplayOpen(ReplayOpenEvent.Post event) { public void postReplayOpen(ReplayOpenEvent.Post event) {
currentTimeline = new SPTimeline(); clearCurrentTimeline();
guiPathing = new GuiPathing(core, this, event.getReplayHandler()); guiPathing = new GuiPathing(core, this, event.getReplayHandler());
} }
@@ -82,11 +99,11 @@ public class ReplayModSimplePathing {
public void setCurrentTimeline(SPTimeline newTimeline) { public void setCurrentTimeline(SPTimeline newTimeline) {
selectedPath = null; selectedPath = null;
currentTimeline = newTimeline; currentTimeline = newTimeline;
updateDefaultInterpolatorType();
} }
public void clearCurrentTimeline() { public void clearCurrentTimeline() {
setCurrentTimeline(new SPTimeline()); setCurrentTimeline(new SPTimeline());
updateDefaultInterpolatorType();
} }
public SPTimeline getCurrentTimeline() { public SPTimeline getCurrentTimeline() {

View File

@@ -71,7 +71,7 @@ public class SPTimeline implements PathingRegistry {
@Getter @Getter
private EntityPositionTracker entityTracker; private EntityPositionTracker entityTracker;
private InterpolatorType defaultInterpolatorType = InterpolatorType.fromString("invalid string returns default"); private InterpolatorType defaultInterpolatorType;
public SPTimeline() { public SPTimeline() {
this(createInitialTimeline()); this(createInitialTimeline());
@@ -340,13 +340,15 @@ public class SPTimeline implements PathingRegistry {
Optional<Interpolator> firstInterpolator = Optional<Interpolator> firstInterpolator =
path.getSegments().stream().findFirst().map(PathSegment::getInterpolator); path.getSegments().stream().findFirst().map(PathSegment::getInterpolator);
// The interpolator of the previous segment // The interpolator that will be lost once we remove the old keyframe and has to be restored afterwards
Optional<Interpolator> interpolatorBefore = Optional<Interpolator> lostInterpolator = path.getSegments().stream().filter(s -> {
path.getSegments().stream().filter(s -> s.getEndKeyframe() == keyframe).findFirst().map(PathSegment::getInterpolator); // If this is the last keyframe,
if (Iterables.getLast(path.getKeyframes()) == keyframe) {
// The interpolator of the following segment return s.getEndKeyframe() == keyframe; // the previous interpolator will be lost
Optional<Interpolator> interpolatorAfter = } else { // otherwise
path.getSegments().stream().filter(s -> s.getStartKeyframe() == keyframe).findFirst().map(PathSegment::getInterpolator); return s.getStartKeyframe() == keyframe; // the following interpolator will be lost
}
}).findFirst().map(PathSegment::getInterpolator);
// First remove the old keyframe // First remove the old keyframe
Change removeChange = create(path, keyframe); Change removeChange = create(path, keyframe);
@@ -369,18 +371,27 @@ public class SPTimeline implements PathingRegistry {
Keyframe newKf = path.getKeyframe(newTime); Keyframe newKf = path.getKeyframe(newTime);
if (Iterables.getLast(path.getKeyframes()) != newKf) { // Unless this is the last keyframe if (Iterables.getLast(path.getKeyframes()) != newKf) { // Unless this is the last keyframe
// the interpolator of the following segment has been lost and needs to be restored // the interpolator of the following segment has been lost and needs to be restored
restoreInterpolatorChange = interpolatorAfter.<Change>flatMap(interpolator -> restoreInterpolatorChange = lostInterpolator.<Change>flatMap(interpolator ->
path.getSegments().stream().filter(s -> s.getStartKeyframe() == newKf).findFirst().map(segment -> path.getSegments().stream().filter(s -> s.getStartKeyframe() == newKf).findFirst().map(segment ->
SetInterpolator.create(segment, interpolator) SetInterpolator.create(segment, interpolator)
) )
).orElseGet(CombinedChange::create); ).orElseGet(CombinedChange::create);
} else { // If it is the last keyframe however, } else { // If it is the last keyframe however,
// the interpolator of the previous segment has been lost and needs to be restored // the interpolator of the previous segment has been lost and needs to be restored
restoreInterpolatorChange = interpolatorBefore.<Change>flatMap(interpolator -> restoreInterpolatorChange = path.getSegments().stream().filter(s -> s.getEndKeyframe() == newKf)
path.getSegments().stream().filter(s -> s.getEndKeyframe() == newKf).findFirst().map(segment -> .findFirst().flatMap(segment -> lostInterpolator.map(interpolator -> {
SetInterpolator.create(segment, interpolator) // additionally, if the interpolation of this keyframe was set to explicit, that property
) // has to be transferred to the start keyframe of the new segment
).orElseGet(CombinedChange::create); if (newKf.getValue(ExplicitInterpolationProperty.PROPERTY).isPresent()) {
return CombinedChange.create(
SetInterpolator.create(segment, interpolator),
UpdateKeyframeProperties.create(path, segment.getStartKeyframe())
.setValue(ExplicitInterpolationProperty.PROPERTY, ObjectUtils.NULL).done()
);
} else {
return SetInterpolator.create(segment, interpolator);
}
})).orElseGet(CombinedChange::create);
} }
restoreInterpolatorChange.apply(timeline); restoreInterpolatorChange.apply(timeline);
@@ -647,7 +658,7 @@ public class SPTimeline implements PathingRegistry {
} catch (IOException e) { } catch (IOException e) {
CrashReport crash = CrashReport.makeCrashReport(e, "Serializing interpolator"); CrashReport crash = CrashReport.makeCrashReport(e, "Serializing interpolator");
CrashReportCategory category = crash.makeCategory("Serializing interpolator"); CrashReportCategory category = crash.makeCategory("Serializing interpolator");
category.setDetail("Interpolator", interpolator::toString); category.addCrashSectionCallable("Interpolator", interpolator::toString);
throw new ReportedException(crash); throw new ReportedException(crash);
} }

View File

@@ -109,6 +109,9 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
if (newTime != time) { if (newTime != time) {
change = CombinedChange.createFromApplied(change, change = CombinedChange.createFromApplied(change,
gui.getMod().getCurrentTimeline().moveKeyframe(path, time, newTime)); gui.getMod().getCurrentTimeline().moveKeyframe(path, time, newTime));
if (gui.getMod().getSelectedPath() == path && gui.getMod().getSelectedTime() == time) {
gui.getMod().setSelected(path, newTime);
}
} }
gui.getMod().getCurrentTimeline().getTimeline().pushChange(change); gui.getMod().getCurrentTimeline().getTimeline().pushChange(change);
close(); close();
@@ -252,6 +255,10 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
xField.getDouble(), yField.getDouble(), zField.getDouble(), xField.getDouble(), yField.getDouble(), zField.getDouble(),
yawField.getFloat(), pitchField.getFloat(), rollField.getFloat() yawField.getFloat(), pitchField.getFloat(), rollField.getFloat()
); );
if (interpolationPanel.getSettingsPanel() == null) {
// The last keyframe doesn't have interpolator settings because there is no segment following it
return positionChange;
}
Interpolator interpolator = interpolationPanel.getSettingsPanel().createInterpolator(); Interpolator interpolator = interpolationPanel.getSettingsPanel().createInterpolator();
if (interpolationPanel.getInterpolatorType() == InterpolatorType.DEFAULT) { if (interpolationPanel.getInterpolatorType() == InterpolatorType.DEFAULT) {
return CombinedChange.createFromApplied(positionChange, timeline.setInterpolatorToDefault(time), return CombinedChange.createFromApplied(positionChange, timeline.setInterpolatorToDefault(time),
@@ -303,6 +310,7 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
dropdown.setSelected(type); // trigger the callback once to display settings panel dropdown.setSelected(type); // trigger the callback once to display settings panel
} else { } else {
setSettingsPanel(InterpolatorType.DEFAULT); setSettingsPanel(InterpolatorType.DEFAULT);
type = InterpolatorType.DEFAULT;
} }
if (getInterpolatorTypeNoDefault(type).getInterpolatorClass().isInstance(interpolator)) { if (getInterpolatorTypeNoDefault(type).getInterpolatorClass().isInstance(interpolator)) {
//noinspection unchecked //noinspection unchecked

View File

@@ -1,10 +1,12 @@
package com.replaymod.simplepathing.gui; package com.replaymod.simplepathing.gui;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.pathing.gui.GuiKeyframeRepository; import com.replaymod.pathing.gui.GuiKeyframeRepository;
import com.replaymod.pathing.player.RealtimeTimelinePlayer; import com.replaymod.pathing.player.RealtimeTimelinePlayer;
import com.replaymod.pathing.properties.CameraProperties; import com.replaymod.pathing.properties.CameraProperties;
@@ -255,6 +257,7 @@ public class GuiPathing {
} }
}; };
private final ReplayMod core;
private final ReplayModSimplePathing mod; private final ReplayModSimplePathing mod;
private final ReplayHandler replayHandler; private final ReplayHandler replayHandler;
private final RealtimeTimelinePlayer player; private final RealtimeTimelinePlayer player;
@@ -264,6 +267,7 @@ public class GuiPathing {
private SettableFuture<Void> entityTrackerFuture; private SettableFuture<Void> entityTrackerFuture;
public GuiPathing(final ReplayMod core, final ReplayModSimplePathing mod, final ReplayHandler replayHandler) { public GuiPathing(final ReplayMod core, final ReplayModSimplePathing mod, final ReplayHandler replayHandler) {
this.core = core;
this.mod = mod; this.mod = mod;
this.replayHandler = replayHandler; this.replayHandler = replayHandler;
this.player = new RealtimeTimelinePlayer(replayHandler); this.player = new RealtimeTimelinePlayer(replayHandler);
@@ -300,9 +304,15 @@ public class GuiPathing {
ListenableFuture<Void> future = player.start(mod.getCurrentTimeline().getTimeline(), startTime); ListenableFuture<Void> future = player.start(mod.getCurrentTimeline().getTimeline(), startTime);
overlay.setCloseable(false); overlay.setCloseable(false);
overlay.setMouseVisible(true); overlay.setMouseVisible(true);
core.printInfoToChat("replaymod.chat.pathstarted");
Futures.addCallback(future, new FutureCallback<Void>() { Futures.addCallback(future, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void result) { public void onSuccess(@Nullable Void result) {
if (future.isCancelled()) {
core.printInfoToChat("replaymod.chat.pathinterrupted");
} else {
core.printInfoToChat("replaymod.chat.pathfinished");
}
overlay.setCloseable(true); overlay.setCloseable(true);
timePath.setActive(true); timePath.setActive(true);
} }
@@ -393,94 +403,90 @@ public class GuiPathing {
} }
}); });
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.keyframerepository", Keyboard.KEY_X, new Runnable() { startLoadingEntityTracker();
@Override }
public void run() {
if (!overlay.isVisible()) {
return;
}
try {
GuiKeyframeRepository gui = new GuiKeyframeRepository(
mod.getCurrentTimeline(), replayHandler.getReplayFile(), mod.getCurrentTimeline().getTimeline());
Futures.addCallback(gui.getFuture(), new FutureCallback<Timeline>() {
@Override
public void onSuccess(Timeline result) {
if (result != null) {
mod.setCurrentTimeline(new SPTimeline(result));
}
}
@Override public void keyframeRepoButtonPressed() {
public void onFailure(Throwable t) { try {
t.printStackTrace(); GuiKeyframeRepository gui = new GuiKeyframeRepository(
core.printWarningToChat("Error loading timeline: " + t.getMessage()); mod.getCurrentTimeline(), replayHandler.getReplayFile(), mod.getCurrentTimeline().getTimeline());
} Futures.addCallback(gui.getFuture(), new FutureCallback<Timeline>() {
});
gui.display();
} catch (IOException e) {
e.printStackTrace();
core.printWarningToChat("Error loading timeline: " + e.getMessage());
}
}
});
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.clearkeyframes", Keyboard.KEY_C, () -> {
GuiYesNoPopup popup = GuiYesNoPopup.open(overlay,
new GuiLabel().setI18nText("replaymod.gui.clearcallback.title").setColor(Colors.BLACK)
).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no");
Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
@Override @Override
public void onSuccess(Boolean delete) { public void onSuccess(Timeline result) {
if (delete) { if (result != null) {
mod.clearCurrentTimeline(); mod.setCurrentTimeline(new SPTimeline(result));
if (entityTracker != null) {
mod.getCurrentTimeline().setEntityTracker(entityTracker);
}
} }
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
t.printStackTrace(); t.printStackTrace();
core.printWarningToChat("Error loading timeline: " + t.getMessage());
} }
}); });
}); gui.display();
} catch (IOException e) {
e.printStackTrace();
core.printWarningToChat("Error loading timeline: " + e.getMessage());
}
}
core.getKeyBindingRegistry().registerRepeatedKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> { public void clearKeyframesButtonPressed() {
// Current replay time GuiYesNoPopup popup = GuiYesNoPopup.open(replayHandler.getOverlay(),
int time = replayHandler.getReplaySender().currentTimeStamp(); new GuiLabel().setI18nText("replaymod.gui.clearcallback.title").setColor(Colors.BLACK)
// Position of the cursor ).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no");
int cursor = timeline.getCursorPosition(); Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
// Get the last time keyframe before the cursor @Override
mod.getCurrentTimeline().getTimePath().getKeyframes().stream() public void onSuccess(Boolean delete) {
.filter(it -> it.getTime() <= cursor).reduce((__, last) -> last).ifPresent(keyframe -> { if (delete) {
// Cursor position at the keyframe mod.clearCurrentTimeline();
int keyframeCursor = (int) keyframe.getTime(); if (entityTracker != null) {
// Replay time at the keyframe mod.getCurrentTimeline().setEntityTracker(entityTracker);
// This is a keyframe from the time path, so it _should_ always have a time property }
int keyframeTime = keyframe.getValue(TimestampProperty.PROPERTY).get(); }
// Replay time passed
int timePassed = time - keyframeTime;
// Speed (set to 1 when shift is held)
double speed = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? 1 : overlay.getSpeedSliderValue();
// Cursor time passed
int cursorPassed = (int) (timePassed / speed);
// Move cursor to new position
timeline.setCursorPosition(keyframeCursor + cursorPassed);
// Deselect keyframe to allow the user to add a new one right away
mod.setSelected(null, 0);
});
});
core.getKeyBindingRegistry().registerRaw(Keyboard.KEY_DELETE, () -> {
if (!overlay.isVisible()) {
return;
} }
if (mod.getSelectedPath() != null) {
updateKeyframe(mod.getSelectedPath()); @Override
public void onFailure(Throwable t) {
t.printStackTrace();
} }
}); });
}
public void syncTimeButtonPressed() {
// Current replay time
int time = replayHandler.getReplaySender().currentTimeStamp();
// Position of the cursor
int cursor = timeline.getCursorPosition();
// Get the last time keyframe before the cursor
mod.getCurrentTimeline().getTimePath().getKeyframes().stream()
.filter(it -> it.getTime() <= cursor).reduce((__, last) -> last).ifPresent(keyframe -> {
// Cursor position at the keyframe
int keyframeCursor = (int) keyframe.getTime();
// Replay time at the keyframe
// This is a keyframe from the time path, so it _should_ always have a time property
int keyframeTime = keyframe.getValue(TimestampProperty.PROPERTY).get();
// Replay time passed
int timePassed = time - keyframeTime;
// Speed (set to 1 when shift is held)
double speed = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? 1 : replayHandler.getOverlay().getSpeedSliderValue();
// Cursor time passed
int cursorPassed = (int) (timePassed / speed);
// Move cursor to new position
timeline.setCursorPosition(keyframeCursor + cursorPassed);
// Deselect keyframe to allow the user to add a new one right away
mod.setSelected(null, 0);
});
}
public void deleteButtonPressed() {
if (mod.getSelectedPath() != null) {
updateKeyframe(mod.getSelectedPath());
}
}
private void startLoadingEntityTracker() {
Preconditions.checkState(entityTrackerFuture == null);
// Start loading entity tracker // Start loading entity tracker
entityTrackerFuture = SettableFuture.create(); entityTrackerFuture = SettableFuture.create();
new Thread(() -> { new Thread(() -> {
@@ -493,12 +499,13 @@ public class GuiPathing {
} }
}); });
logger.info("Loaded entity tracker in " + (System.currentTimeMillis() - start) + "ms"); logger.info("Loaded entity tracker in " + (System.currentTimeMillis() - start) + "ms");
} catch (IOException e) { } catch (Throwable e) {
logger.error("Loading entity tracker:", e); logger.error("Loading entity tracker:", e);
mod.getCore().runLater(() -> { mod.getCore().runLater(() -> {
mod.getCore().printWarningToChat("Error loading entity tracker: %s", e.getLocalizedMessage()); mod.getCore().printWarningToChat("Error loading entity tracker: %s", e.getLocalizedMessage());
entityTrackerFuture.setException(e); entityTrackerFuture.setException(e);
}); });
return;
} }
entityTracker = tracker; entityTracker = tracker;
mod.getCore().runLater(() -> { mod.getCore().runLater(() -> {
@@ -557,7 +564,9 @@ public class GuiPathing {
@Override @Override
public void onFailure(@Nonnull Throwable t) { public void onFailure(@Nonnull Throwable t) {
popup.close(); String message = "Failed to load entity tracker, pathing will be unavailable.";
GuiReplayOverlay overlay = replayHandler.getOverlay();
Utils.error(LOGGER, overlay, CrashReport.makeCrashReport(t, message), popup::close);
} }
}); });
return false; return false;

View File

@@ -8,7 +8,7 @@ import com.replaymod.replay.events.ReplayCloseEvent;
import com.replaymod.replay.events.ReplayOpenEvent; import com.replaymod.replay.events.ReplayOpenEvent;
import com.replaymod.simplepathing.ReplayModSimplePathing; import com.replaymod.simplepathing.ReplayModSimplePathing;
import com.replaymod.simplepathing.Setting; import com.replaymod.simplepathing.Setting;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
@@ -23,7 +23,7 @@ public class PathPreview {
} }
public void register() { public void register() {
MinecraftForge.EVENT_BUS.register(this); FMLCommonHandler.instance().bus().register(this);
ReplayMod core = mod.getCore(); ReplayMod core = mod.getCore();
mod.getCore().getKeyBindingRegistry().registerKeyBinding("replaymod.input.pathpreview", Keyboard.KEY_H, () -> { mod.getCore().getKeyBindingRegistry().registerKeyBinding("replaymod.input.pathpreview", Keyboard.KEY_H, () -> {

View File

@@ -17,7 +17,7 @@ import com.replaymod.simplepathing.gui.GuiPathing;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
@@ -204,7 +204,7 @@ public class PathPreviewRenderer {
if (distanceSquared(view, pos2) > renderDistanceSquared) return; if (distanceSquared(view, pos2) > renderDistanceSquared) return;
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
VertexBuffer vertexBuffer = tessellator.getBuffer(); WorldRenderer vertexBuffer = tessellator.getWorldRenderer();
vertexBuffer.setTranslation(-view.getLeft(), -view.getMiddle(), -view.getRight()); vertexBuffer.setTranslation(-view.getLeft(), -view.getMiddle(), -view.getRight());
vertexBuffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR); vertexBuffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR);
@@ -231,7 +231,7 @@ public class PathPreviewRenderer {
Triple<Double, Double, Double> pos, Triple<Double, Double, Double> pos,
Keyframe keyframe) { Keyframe keyframe) {
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
VertexBuffer vertexBuffer = tessellator.getBuffer(); WorldRenderer vertexBuffer = tessellator.getWorldRenderer();
vertexBuffer.setTranslation(0, 0, 0); vertexBuffer.setTranslation(0, 0, 0);
mc.renderEngine.bindTexture(TEXTURE); mc.renderEngine.bindTexture(TEXTURE);
@@ -280,7 +280,7 @@ public class PathPreviewRenderer {
Triple<Double, Double, Double> pos, Triple<Double, Double, Double> pos,
Triple<Float, Float, Float> rot) { Triple<Float, Float, Float> rot) {
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
VertexBuffer vertexBuffer = tessellator.getBuffer(); WorldRenderer vertexBuffer = tessellator.getWorldRenderer();
vertexBuffer.setTranslation(0, 0, 0); vertexBuffer.setTranslation(0, 0, 0);
mc.renderEngine.bindTexture(CAMERA_HEAD); mc.renderEngine.bindTexture(CAMERA_HEAD);

View File

@@ -1,7 +1,7 @@
# To apply changes in this file run: ./gradlew clean setupDecompWorkspace idea # To apply changes in this file run: ./gradlew clean setupDecompWorkspace idea
# Minecraft # Minecraft
public-f net.minecraft.client.Minecraft field_71428_T # timer public net.minecraft.client.Minecraft field_71428_T # timer
public net.minecraft.client.Minecraft field_83002_am # debugCrashKeyPressTime public net.minecraft.client.Minecraft field_83002_am # debugCrashKeyPressTime
public net.minecraft.client.Minecraft field_71467_ac # rightClickDelayTimer public net.minecraft.client.Minecraft field_71467_ac # rightClickDelayTimer
public net.minecraft.client.Minecraft field_71423_H # systemTime public net.minecraft.client.Minecraft field_71423_H # systemTime
@@ -21,23 +21,23 @@ public net.minecraft.client.Minecraft func_147112_ai()V # middleClickMouse
public net.minecraft.client.Minecraft func_147115_a(Z)V # sendClickBlockToController public net.minecraft.client.Minecraft func_147115_a(Z)V # sendClickBlockToController
# Packets # Packets
public net.minecraft.network.play.server.SPacketSpawnMob field_149043_l # dataManager public net.minecraft.network.play.server.S0FPacketSpawnMob field_149043_l # dataWatcher
public net.minecraft.network.play.server.SPacketSpawnPlayer field_148960_i # watcher
#public net.minecraft.network.play.server.S38PacketPlayerListItem field_179964_d # profile #public net.minecraft.network.play.server.S38PacketPlayerListItem field_179964_d # profile
# public net.minecraft.network.play.server.S1DPacketEntityEffect field_149434_a # entityId public net.minecraft.network.play.server.S1DPacketEntityEffect field_149434_a # entityId
# public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149379_a # entityId public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149379_a # entityId
# public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149378_b # list public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149378_b # list
# public net.minecraft.network.play.server.S0BPacketAnimation field_148981_a # entityId public net.minecraft.network.play.server.S0BPacketAnimation field_148981_a # entityId
# public net.minecraft.network.play.server.S02PacketChat field_179842_b # position public net.minecraft.network.play.server.S02PacketChat field_179842_b # position
# public net.minecraft.network.play.server.S19PacketEntityStatus field_149164_a # entityId public net.minecraft.network.play.server.S19PacketEntityStatus field_149164_a # entityId
# public net.minecraft.network.play.server.S18PacketEntityTeleport * public net.minecraft.network.play.server.S0CPacketSpawnPlayer *
# public net.minecraft.network.play.server.S14PacketEntity * public net.minecraft.network.play.server.S18PacketEntityTeleport *
# public net.minecraft.network.play.server.S19PacketEntityHeadLook * public net.minecraft.network.play.server.S14PacketEntity *
# public net.minecraft.network.play.server.S12PacketEntityVelocity * public net.minecraft.network.play.server.S19PacketEntityHeadLook *
# public net.minecraft.network.play.server.S04PacketEntityEquipment * public net.minecraft.network.play.server.S12PacketEntityVelocity *
# public net.minecraft.network.play.server.S1BPacketEntityAttach * public net.minecraft.network.play.server.S04PacketEntityEquipment *
# public net.minecraft.network.play.server.S0DPacketCollectItem * public net.minecraft.network.play.server.S1BPacketEntityAttach *
# public net.minecraft.network.play.server.S13PacketDestroyEntities * public net.minecraft.network.play.server.S0DPacketCollectItem *
public net.minecraft.network.play.server.S13PacketDestroyEntities *
# GuiTextField # GuiTextField
public net.minecraft.client.gui.GuiTextField field_146216_j # text public net.minecraft.client.gui.GuiTextField field_146216_j # text
@@ -61,7 +61,7 @@ public net.minecraft.client.renderer.RenderGlobal field_175009_l # chunksToUpdat
public net.minecraft.client.renderer.RenderGlobal field_72755_R # renderInfos public net.minecraft.client.renderer.RenderGlobal field_72755_R # renderInfos
public net.minecraft.client.renderer.RenderGlobal field_175009_l # chunksToUpdate public net.minecraft.client.renderer.RenderGlobal field_175009_l # chunksToUpdate
public net.minecraft.client.renderer.RenderGlobal field_72740_G # renderEntitiesStartupCounter public net.minecraft.client.renderer.RenderGlobal field_72740_G # renderEntitiesStartupCounter
# public net.minecraft.client.renderer.RenderGlobal func_174983_a(Lnet.minecraft.util.BlockPos;Lnet.minecraft.client.renderer.chunk.RenderChunk;)Z # isPositionInRenderChunk public net.minecraft.client.renderer.RenderGlobal func_174983_a(Lnet.minecraft.util.BlockPos;Lnet.minecraft.client.renderer.chunk.RenderChunk;)Z # isPositionInRenderChunk
public net.minecraft.client.renderer.RenderGlobal$ContainerLocalRenderInformation public net.minecraft.client.renderer.RenderGlobal$ContainerLocalRenderInformation
#public net.minecraft.client.renderer.RenderGlobal$ContainerLocalRenderInformation field_178553_a # renderChunk #public net.minecraft.client.renderer.RenderGlobal$ContainerLocalRenderInformation field_178553_a # renderChunk
@@ -88,17 +88,17 @@ public net.minecraft.entity.player.EntityPlayer *
public net.minecraft.client.entity.EntityOtherPlayerMP * public net.minecraft.client.entity.EntityOtherPlayerMP *
# EffectRenderer # EffectRenderer
# public net.minecraft.client.particle.EffectRenderer field_110737_b # particleTextures public net.minecraft.client.particle.EffectRenderer field_110737_b # particleTextures
# public net.minecraft.client.particle.EffectRenderer field_78876_b # fxLayers public net.minecraft.client.particle.EffectRenderer field_78876_b # fxLayers
# IntegratedServer # IntegratedServer
public net.minecraft.server.integrated.IntegratedServer field_71348_o # isGamePaused public net.minecraft.server.integrated.IntegratedServer field_71348_o # isGamePaused
# GameSettings # GameSettings
public-f net.minecraft.client.settings.GameSettings field_186714_aM # soundLevels public net.minecraft.client.settings.GameSettings field_151446_aD # mapSoundLevels
# Frustum # Frustum
public-f net.minecraft.client.renderer.culling.Frustum field_78552_a # clippingHelper public net.minecraft.client.renderer.culling.Frustum field_78552_a # clippingHelper
# CrashReportCategory # CrashReportCategory
public net.minecraft.crash.CrashReportCategory field_85077_c # children public net.minecraft.crash.CrashReportCategory field_85077_c # children
@@ -117,12 +117,5 @@ public net.minecraft.client.renderer.GlStateManager$FogState
public net.minecraft.client.renderer.GlStateManager$BooleanState public net.minecraft.client.renderer.GlStateManager$BooleanState
public net.minecraft.client.renderer.GlStateManager$BooleanState * public net.minecraft.client.renderer.GlStateManager$BooleanState *
# EntityLivingBase
public net.minecraft.entity.EntityLivingBase field_184621_as # HAND_STATES
public net.minecraft.entity.EntityLivingBase field_184628_bn # activeItemStackUseCount
# EntityLivingBase
public net.minecraft.client.particle.Particle *
# Example # Example
# public net.minecraft.package.ClassName func_some_id(Ljava/lang/Class;IZS)V # methodName # public net.minecraft.package.ClassName func_some_id(Ljava/lang/Class;IZS)V # methodName

View File

@@ -1,539 +0,0 @@
replaymod.title=Replay Mod
#Website API translations
replaymod.api.parammissing=Required parameter missing: %1$s
replaymod.api.invalidvalue=Invalid value passed for parameter %1$s - Possible values: %2$s
replaymod.api.invalidlogin=Invalid login data provided
replaymod.api.invalidauthkey=Invalid authentication key provided
replaymod.api.notenoughspace=Not enough space left to upload this file. Consider purchasing Premium on ReplayMod.com
replaymod.api.invalidfile=This file is invalid
replaymod.api.fileduplicate=This file has already been uploaded
replaymod.api.filenoexist=This file does not exist
replaymod.api.nopermissions=You don't have permission to access this file
replaymod.api.alreadyrated=You already rated this file
replaymod.api.usernoexist=This user does not exist
replaymod.api.invalidcategory=Invalid file category
replaymod.api.nofilesuploaded=No files uploaded
replaymod.api.toomanyvalues=Too many values passed for %1$s
replaymod.api.noparameters=At least one parameter required. Parameters: %1$s
replaymod.api.invalidfilename=File name must be between 5 and 30 characters long
replaymod.api.unknownlang=This language has not been translated
replaymod.api.zippingerror=An error occured while zipping the language files
replaymod.api.usernameexists=This username is already taken
replaymod.api.mailexists=This Email address is already linked to an account
replaymod.api.invalidmail=Invalid Email Address
replaymod.api.nopermissions=You don't have permission to do this
replaymod.api.authfailed=Authentication to the Minecraft Session Servers failed
replaymod.api.mcuserexists=A ReplayMod account is already associated with this Minecraft account
replaymod.api.filetoobig=The uploaded file is too big
replaymod.api.usernametoolong=The username is too long
replaymod.api.descriptiontoolong=The description is too long
replaymod.api.passwordlength=The password has to be between 5 and 1024 characters long
replaymod.api.suspended=Your account has been suspended
#All of the chat messages
replaymod.chat.recordingstarted=Recording started
replaymod.chat.recordingfailed=Failed to start recording
replaymod.chat.savingthumb=Saving Thumbnail...
replaymod.chat.savedthumb=Thumbnail has been successfully saved
replaymod.chat.failedthumb=Thumbnail could not be saved
replaymod.chat.addedmarker=Event Marker has been added
#Chat messages displayed in Replay Viewer
replaymod.chat.morekeyframes=At least 2 position keyframes and 2 time keyframes required
replaymod.chat.negativetime=Camera Paths can't play time backwards. Please consult your nearest time lord for assistance.
replaymod.chat.pathstarted=Camera Path started
replaymod.chat.pathfinished=Camera Path finished
replaymod.chat.pathinterrupted=Camera Path canceled
#Replay Categories
replaymod.category=Category
replaymod.category.survival=Survival
replaymod.category.minigame=Minigame
replaymod.category.build=Build
replaymod.category.misc=Miscellaneous
#Common GUI-related strings
replaymod.gui.replay=Replay
replaymod.gui.login=Login
replaymod.gui.logout=Logout
replaymod.gui.cancel=Cancel
replaymod.gui.username=Username
replaymod.gui.password=Password
replaymod.gui.back=Back
replaymod.gui.duration=Duration
replaymod.gui.load=Load
replaymod.gui.download=Download
replaymod.gui.like=Like
replaymod.gui.dislike=Dislike
replaymod.gui.removelike=Remove Like
replaymod.gui.removedislike=Remove Dislike
replaymod.gui.save=Save
replaymod.gui.upload=Upload
replaymod.gui.rename=Rename
replaymod.gui.remove=Remove
replaymod.gui.add=Add
replaymod.gui.start=Start
replaymod.gui.end=End
replaymod.gui.delete=Delete
replaymod.gui.recording=Recording
replaymod.gui.speed=Speed
replaymod.gui.register=Register
replaymod.gui.mail=Email Address
replaymod.gui.loading=Loading
replaymod.gui.pleasewait=Please wait
replaymod.gui.render=Render
replaymod.gui.iphidden=Server IP Hidden
replaymod.gui.overwrite=Overwrite
replaymod.gui.saveas=Save as ...
replaymod.gui.done=Done
replaymod.gui.close=Close
replaymod.gui.notagain=Don't show again
replaymod.gui.renderdonetitle=Video rendered
replaymod.gui.openfolder=Open Video Folder
replaymod.gui.youtubeupload=Upload to YouTube
replaymod.gui.renderdone1=Your video was successfully rendered.
replaymod.gui.renderdone2=How would you like to proceed?
replaymod.gui.videotitle=Title
replaymod.gui.videodescription=Description
replaymod.gui.videotags=Tags,Tags,Tags
replaymod.gui.videothumbnail=Video Thumbnail
replaymod.gui.videovisibility.private=Private
replaymod.gui.videovisibility.unlisted=Unlisted
replaymod.gui.videovisibility.public=Public
replaymod.gui.ytuploadprogress.auth=[1/4] Authorization
replaymod.gui.ytuploadprogress.prepare_video=[2/4] Preparing video: %%d%%%%
replaymod.gui.ytuploadprogress.upload=[3/4] Uploading: %%d%%%%
replaymod.gui.ytuploadprogress.cleanup=[4/4] Cleanup
replaymod.gui.ytuploadprogress.done=Done: %s
replaymod.gui.titleempty=Title cannot be empty
replaymod.gui.videothumbnailtoolarge=Thumbnail size exceeds 2MB
replaymod.gui.videothumbnailformat=Thumbnail has to be either JPEG or PNG format
replaymod.gui.original=Original
replaymod.gui.modified=Modified
replaymod.gui.outdated=There is a newer Replay Mod version available. Please download it from replaymod.com
replaymod.gui.hours=h
replaymod.gui.minutes=min
replaymod.gui.seconds=sec
replaymod.gui.milliseconds=ms
replaymod.gui.pitch=Pitch
replaymod.gui.yaw=Yaw
replaymod.gui.roll=Roll
replaymod.gui.camera=Camera
replaymod.gui.position=Position
replaymod.gui.unknownerror=An unknown error occured
replaymod.gui.exit=Exit Replay
replaymod.gui.java=Java 1.7 or newer required
replaymod.gui.morereplays=At least one Replay required
replaymod.gui.loggedin=LOGGED IN
replaymod.gui.loggedout=LOGGED OUT
replaymod.gui.mainmenu=Main Menu
replaymod.gui.settings=Settings
replaymod.gui.keyframerepo.delete=Are you sure you want to delete these keyframes?
replaymod.gui.restorereplay1=It seems like Minecraft has not quit normally.
replaymod.gui.restorereplay2=The Replay "%1$s" was not saved correctly.
replaymod.gui.restorereplay3=Do you wish to recover it?
replaymod.gui.loadentitytracker=Loading entity positions: %%d%%%%
#Only change these if it's neccessary
replaymod.gui.replayviewer=Replay Viewer
replaymod.gui.replaycenter=Replay Center
replaymod.gui.replaysettings=Replay Settings
replaymod.gui.replayeditor=Replay Editor
#Login GUI
replaymod.gui.login.title=Login to ReplayMod.com
replaymod.gui.login.logging=Logging in...
replaymod.gui.login.incorrect=Incorrect username or password
replaymod.gui.login.connectionerror=Could not connect to ReplayMod.com
replaymod.gui.login.skip=Skip
replaymod.gui.register.title=Register on ReplayMod.com
replaymod.gui.register.confirmpw=Confirm Password
replaymod.gui.register.disclaimer=By registering an account, you agree to the Replay Mod's Terms of Service: https://replaymod.com/legal/terms
replaymod.gui.register.error.nomatch=Passwords don't match
replaymod.gui.register.error.shortusername=Username has to be at least 5 characters long
replaymod.gui.register.error.invalidname=Username may only contain letters and numbers
replaymod.gui.register.error.shortpw=Password has to be at least 5 characters long
replaymod.gui.register.error.longpw=Password has to be at most 1024 characters long
replaymod.gui.register.error.authfailed=Could not authenticate your Minecraft account
#Replay Viewer GUI
replaymod.gui.viewer.rename.title=Rename Replay
replaymod.gui.viewer.rename.name=Replay Name
replaymod.gui.viewer.replayfolder=Open Replay Folder...
replaymod.gui.viewer.noauth=Log in to upload a Replay
replaymod.gui.viewer.alreadyuploaded=This Replay has already been uploaded
replaymod.gui.viewer.delete.linea=Are you sure you want to delete this replay?
replaymod.gui.viewer.delete.failed1=Your OS did not allow us to move the replay. There is
replaymod.gui.viewer.delete.failed2=nothing we can do about this. You have to do it yourself.
replaymod.gui.viewer.delete.lineb='%1$s' will be lost forever! (a long time!)
replaymod.gui.viewer.download.title=Downloading Replay File...
replaymod.gui.viewer.download.message=Please wait while "%1$s" is being downloaded.
replaymod.gui.viewer.chooser.title=Choose Replay File
replaymod.gui.viewer.chooser.message=You have a modified version of "%1$s" on your computer. Which Replay do you want to load?
replaymod.gui.login.noacc=Don't have an account yet?
#Replay Center GUI
replaymod.gui.center.logoutcallback=Do you really want to log out?
replaymod.gui.center.top.recent=Recent
replaymod.gui.center.top.best=Best
replaymod.gui.center.top.downloaded=Downloaded
replaymod.gui.center.top.favorited=Favorited
replaymod.gui.center.top.search=Search
replaymod.gui.center.search.filters=Search Filters
replaymod.gui.center.search.gametype=Gametype
replaymod.gui.center.search.order=Sort by
replaymod.gui.center.search.order.best=Best
replaymod.gui.center.search.order.recent=Recent
replaymod.gui.center.search.name=Replay Name
replaymod.gui.center.search.server=Server IP (Multiplayer only)
replaymod.gui.center.search.category=Filter by Category
replaymod.gui.center.search.version=Filter by Version
replaymod.gui.center.downloadrequired=Download the Replay to rate or favorite it
replaymod.gui.center.favorite=Favorite
replaymod.gui.center.unfavorite=Unfavorite
replaymod.gui.center.favorites=Favorites
replaymod.gui.center.author=by %1$s%2$s
#Upload GUI
replaymod.gui.upload.title=Upload File
replaymod.gui.upload.start=Start Upload
replaymod.gui.upload.cancel=Cancel Upload
replaymod.gui.upload.tagshint=Tags separated by comma
replaymod.gui.upload.namehint=Replay Title
replaymod.gui.upload.descriptionhint=Description
replaymod.gui.upload.duration=Duration: %02dm%02ds
replaymod.gui.upload.uploading=Uploading...
replaymod.gui.upload.success=File has been successfully uploaded
replaymod.gui.upload.canceled=Upload has been canceled
replaymod.gui.upload.hideip2=Hide Server IP (%s)
replaymod.gui.upload.nothumbnail=Missing Thumbnail. Please create a Thumbnail in the Replay using the %1$s key.
replaymod.gui.upload.tryagain=Try again?
replaymod.gui.upload.error.name.length=The Replay Name has to be between 5 and 30 characters long
replaymod.gui.upload.error.name=The Replay Name may not contain special characters
replaymod.gui.upload.error.tags=Tags may only contain letters and are separated using a comma
#Replay Editor
replaymod.gui.editor.replayfile=Replay File
replaymod.gui.editor.savemode.override=Replace Source File
replaymod.gui.editor.savemode.newfile=Save to new File
replaymod.gui.editor.trim.title=Trim Replay
replaymod.gui.editor.connect.title=Connect Replays
replaymod.gui.editor.modify.title=Modify Replay
replaymod.gui.editor.trim.description=Removes the beginning and end of a Replay File, only keeping the Replay between the given timestamps
replaymod.gui.editor.connect.description=Connects multiple Replays in the specified order
replaymod.gui.editor.modify.description=Provides several filters to modify Replay Files
replaymod.gui.editor.trim.marker=Select Event Marker
replaymod.gui.editor.progress.title=Editing Replay File...
replaymod.gui.editor.progress.pleasewait=Please wait while the Replay is being edited.
replaymod.gui.editor.progress.status.initializing=Initializing
replaymod.gui.editor.progress.status.writing.raw=Rewriting Replay...
replaymod.gui.editor.progress.status.writing.final=Writing File to disk...
replaymod.gui.editor.progress.status.finished=Finished Editing!
replaymod.gui.editor.disclaimer=The Replay Editor is an experimental feature and may contain bugs.
#Cancel Replay GUI
replaymod.gui.cancelrender.title=Cancel Rendering
replaymod.gui.cancelrender.message=Are you sure that you want to cancel the current rendering process?
#Saving Replay GUI
replaymod.gui.replaysaving.title=Saving Replay File...
replaymod.gui.replaysaving.message=Please wait while your recent Replay is being saved.
replaymod.gui.replaymodified.message=Replay was modified. Would you like to save the changes?
replaymod.gui.replaymodified.yes=Save Modified Replay
replaymod.gui.replaymodified.no=Discard Changes
replaymod.gui.replaymodified.warning1=A Replay named "%1$s" already exists.
replaymod.gui.replaymodified.warning2=Are you sure you want to replace it?
#Player Overview GUI
replaymod.gui.playeroverview.visible=Visible
replaymod.gui.playeroverview.hideall=Hide all
replaymod.gui.playeroverview.showall=Show all
replaymod.gui.playeroverview.spectate=Spectate Player
replaymod.gui.playeroverview.remembersettings=Remember Hidden Players
replaymod.gui.playeroverview.remembersettings.description=Saves Player Visibility in Replay File
#Replay Mod Settings GUI
replaymod.gui.settings.title=Replay Mod Settings
replaymod.gui.settings.interpolation.linear=Linear
replaymod.gui.settings.interpolation.cubic=Cubic
replaymod.gui.settings.interpolation.catmullrom=Catmull Rom
replaymod.gui.settings.bitrate=Video Bitrate
replaymod.gui.settings.framerate=Video Framerate
replaymod.gui.settings.notifications=Enable Notifications
replaymod.gui.settings.recordserver=Record Server
replaymod.gui.settings.recordsingleplayer=Record Singleplayer
replaymod.gui.settings.indicator=Recording Indicator
replaymod.gui.settings.lighting=Ambient Lighting
replaymod.gui.settings.forcechunks=Force Render Chunks
replaymod.gui.settings.resources=Server Resource Packs
replaymod.gui.settings.interpolation=Path Interpolation
replaymod.gui.settings.linearinterpolation=Linear Path Interpolation
replaymod.gui.settings.pathpreview=Show Path Preview
replaymod.gui.settings.keyframecleancallback=Clear Confirmation
replaymod.gui.settings.renderinvisible=Render invisible Entities
replaymod.gui.settings.camera=Camera
replaymod.gui.settings.showchat=Show Chat
replaymod.gui.settings.interpolator=Default Interpolator
replaymod.gui.settings.warning.linea=WARNING: Recording settings will be
replaymod.gui.settings.warning.lineb=applied the next time you join a world.
#Replay Mod Keybindings
replaymod.input.lighting=Toggle Lighting
replaymod.input.thumbnail=Capture Thumbnail
replaymod.input.playeroverview=Player Overview
replaymod.input.clearkeyframes=Clear Keyframes
replaymod.input.synctimeline=Synchronize Timeline
replaymod.input.keyframerepository=Open Keyframe Presets
replaymod.input.rollclockwise=Roll Clockwise
replaymod.input.rollcounterclockwise=Roll Counterclockwise
replaymod.input.resettilt=Reset Camera Tilt
replaymod.input.playpause=Play/Pause Replay
replaymod.input.marker=Add Event Marker
replaymod.input.pathpreview=Toggle Path Preview
replaymod.input.assetmanager=Asset Manager
replaymod.input.objectmanager=Object Manager
replaymod.input.interpolation=Toggle Interpolation
replaymod.input.settings=ReplayMod Settings
# CameraControllers
replaymod.camera.classic=Classic
replaymod.camera.vanilla=Vanilla-ish
#Keyframe Presets GUI
replaymod.gui.keyframerepository.title=Keyframe Repository
replaymod.gui.keyframerepository.presets=Keyframe Presets
replaymod.gui.keyframerepository.positionkeyframes=Position Keyframes
replaymod.gui.keyframerepository.timekeyframes=Time Keyframes
replaymod.gui.keyframerepository.savecurrent=Save current Path
replaymod.gui.keyframerepository.noentries=No Presets available
replaymod.gui.keyframerepository.preset.defaultname=New Keyframe Preset
replaymod.gui.keyframerepository.duplicate=This Preset already exists
#Edit Keyframe GUI
replaymod.gui.editkeyframe.title.pos=Edit Position Keyframe
replaymod.gui.editkeyframe.title.time=Edit Time Keyframe
replaymod.gui.editkeyframe.title.marker=Edit Event Marker
replaymod.gui.editkeyframe.title.spec=Edit Spectator Keyframe
replaymod.gui.editkeyframe.xpos=X Position
replaymod.gui.editkeyframe.ypos=Y Position
replaymod.gui.editkeyframe.zpos=Z Position
replaymod.gui.editkeyframe.campitch=Camera Pitch
replaymod.gui.editkeyframe.camyaw=Camera Yaw
replaymod.gui.editkeyframe.camroll=Camera Roll
replaymod.gui.editkeyframe.timelineposition=Timeline Position
replaymod.gui.editkeyframe.markername=Event Marker Name
replaymod.gui.editkeyframe.timestamp=Time Index
replaymod.gui.editkeyframe.spec.method=Spectating Method
replaymod.gui.editkeyframe.spec.method.firstperson=First Person
replaymod.gui.editkeyframe.spec.method.shoulder=Shoulder Camera
replaymod.gui.editkeyframe.spec.method.shoulder.distance=Camera Distance
replaymod.gui.editkeyframe.spec.method.shoulder.pitch=Pitch Offset
replaymod.gui.editkeyframe.spec.method.shoulder.yaw=Rotation Angle
replaymod.gui.editkeyframe.spec.method.shoulder.smoothness=Path Smoothness
replaymod.gui.editkeyframe.interpolator=Interpolator
replaymod.gui.editkeyframe.interpolator.default.name=Default Interpolator
replaymod.gui.editkeyframe.interpolator.default.desc=Uses the default interpolator defined in the Replay Mod Settings.
replaymod.gui.editkeyframe.interpolator.catmullrom.name=Catmull Rom Spline Interpolator
replaymod.gui.editkeyframe.interpolator.catmullrom.desc=Calculates a catmull rom spline with customizable alpha value for precise tightness and smoothness of the camera path.
replaymod.gui.editkeyframe.interpolator.catmullrom.alpha=Alpha:
replaymod.gui.editkeyframe.interpolator.cubic.name=Cubic Spline Interpolator
replaymod.gui.editkeyframe.interpolator.cubic.desc=Calculates a cubic equation matrix for all points for a smooth camera path.
replaymod.gui.editkeyframe.interpolator.linear.name=Linear Interpolator
replaymod.gui.editkeyframe.interpolator.linear.desc=Draws straight lines between the keyframes.
#Render Settings GUI
replaymod.gui.rendersettings.title=Rendering Options
replaymod.gui.rendersettings.renderer=Rendering Method
replaymod.gui.rendersettings.renderer.default=Default Rendering
replaymod.gui.rendersettings.renderer.tiled=Tiled Rendering
replaymod.gui.rendersettings.renderer.stereoscopic=Stereoscopic Rendering
replaymod.gui.rendersettings.renderer.cubic=Cubic Rendering
replaymod.gui.rendersettings.renderer.equirectangular=Equirectangular Rendering
replaymod.gui.rendersettings.renderer.ods=ODS Rendering
replaymod.gui.rendersettings.renderer.default.description=Renders the video in the specified resolution. Fastest Rendering Option.
replaymod.gui.rendersettings.renderer.stereoscopic.description=Renders the video as a stereoscopic (side-by-side) 3D movie, usable by different 3D technologies. The image for one eye is half the width of the video
replaymod.gui.rendersettings.renderer.cubic.description=Renders the video with a 360 degree panoramic view, using Cubic Projection. This is usable by several 360 degree video players (and the Oculus Rift), for example VR Player.
replaymod.gui.rendersettings.renderer.equirectangular.description=Renders the video with a 360 degree panoramic view, using Equirectangular Projection. This is usable by YouTube's new 360 degree video function, and several video players (and the Oculus Rift), for example VR Player.
replaymod.gui.rendersettings.renderer.ods.description=Renders the video with a stereoscopic 3D 360 degree panoramic view, also called VR Video. This is usable by YouTube's new VR video function.
replaymod.gui.rendersettings.customresolution=Video Resolution
replaymod.gui.rendersettings.customresolution.warning.cubic=Cubic Rendering requires an aspect ratio of 4:3
replaymod.gui.rendersettings.customresolution.warning.equirectangular=Equirectangular Rendering requires an aspect ratio of 2:1
replaymod.gui.rendersettings.customresolution.warning.yuv420=For this preset, the width and height values need to be even numbers
replaymod.gui.rendersettings.customresolution.warning.ods=ODS Rendering requires an aspect ratio of 1:1
replaymod.gui.rendersettings.resolution=Video Resolution
replaymod.gui.rendersettings.interpolation=Path Interpolation
replaymod.gui.rendersettings.forcechunks=Force Render Chunks
replaymod.gui.rendersettings.framerate=Video Framerate
replaymod.gui.rendersettings.quality=Video Quality
replaymod.gui.rendersettings.chromakey=Chroma Keying
replaymod.gui.rendersettings.skycolor=Sky Color
replaymod.gui.rendersettings.nametags=Render Name Tags
replaymod.gui.rendersettings.outputfile=Output File
replaymod.gui.rendersettings.360metadata=Inject 360° Metadata
replaymod.gui.rendersettings.360metadata.error=360° Metadata can only be added to Equirectangular videos in MP4 format
replaymod.gui.rendersettings.command=Command
replaymod.gui.rendersettings.arguments=Command Line Arguments
replaymod.gui.rendersettings.ffmpeg.description=If you are an advanced user, you can customize the command line parameters used to export the video. For more information, check http://replaymod.com/docs
replaymod.gui.rendersettings.stabilizecamera=Stabilize Camera
replaymod.gui.rendersettings.exportyoutube=Export for YouTube
replaymod.gui.rendersettings.video=Video Settings
replaymod.gui.rendersettings.advanced=Advanced Settings
replaymod.gui.rendersettings.commandline=Command Line Settings
replaymod.gui.rendersettings.presets=Encoding Presets
replaymod.gui.rendersettings.presets.mp4.custom=MP4 - Custom Bitrate
replaymod.gui.rendersettings.presets.mp4.high=MP4 - High Quality
replaymod.gui.rendersettings.presets.mp4.default=MP4 - Default Quality
replaymod.gui.rendersettings.presets.mp4.potato=MP4 - Potato Quality
replaymod.gui.rendersettings.presets.webm.custom=WEBM - Custom Bitrate
replaymod.gui.rendersettings.presets.mkv.lossless=MKV - Lossless
replaymod.gui.rendersettings.presets.png=PNG Sequence
replaymod.gui.rendersettings.antialiasing=Anti-Aliasing
replaymod.gui.rendersettings.antialiasing.none=None
replaymod.gui.rendersettings.antialiasing.x2=2x
replaymod.gui.rendersettings.antialiasing.x4=4x
replaymod.gui.rendersettings.antialiasing.x8=8x
#Render Queue GUI
replaymod.gui.renderqueue.title=Render Queue
replaymod.gui.renderqueue.open=Open Queue
replaymod.gui.renderqueue.jobname=Job Name:
replaymod.gui.renderqueue.add=Add current configuration
#Rendering GUI
replaymod.gui.rendering.title=Rendering Video
replaymod.gui.rendering.pause=Pause Rendering
replaymod.gui.rendering.resume=Resume Rendering
replaymod.gui.rendering.cancel=Cancel Rendering
replaymod.gui.rendering.cancel.callback=Are you sure?
replaymod.gui.rendering.preview=Show Preview (Performance might suffer)
replaymod.gui.rendering.progress=Frames rendered: %1$d / %2$d
replaymod.gui.rendering.timetaken=Render Time
replaymod.gui.rendering.timeleft=Time Left
#Render Errors
replaymod.gui.rendering.error.title=Rendering Failed
replaymod.gui.rendering.error.optifine=Please uninstall Optifine before rendering.
replaymod.gui.rendering.error.message=To render a video, you need to have ffmpeg installed. http://ffmpeg.org
#Ingame Menu
replaymod.gui.ingame.menu.addposkeyframe=Add Position Keyframe
replaymod.gui.ingame.menu.removeposkeyframe=Remove Position Keyframe
replaymod.gui.ingame.menu.addspeckeyframe=Add Spectator Keyframe
replaymod.gui.ingame.menu.removespeckeyframe=Remove Spectator Keyframe
replaymod.gui.ingame.menu.addtimekeyframe=Add Time Keyframe
replaymod.gui.ingame.menu.removetimekeyframe=Remove Time Keyframe
replaymod.gui.ingame.menu.pause=Pause Replay
replaymod.gui.ingame.menu.unpause=Unpause Replay
replaymod.gui.ingame.menu.renderpath=Render Camera Path
replaymod.gui.ingame.menu.playpath=Play Camera Path from Cursor Position
replaymod.gui.ingame.menu.playpathfromstart=Play Camera Path from Start
replaymod.gui.ingame.menu.pausepath=Pause Camera Path
replaymod.gui.ingame.menu.zoomin=Zoom in
replaymod.gui.ingame.menu.zoomout=Zoom out
replaymod.gui.ingame.unnamedmarker=Unnamed Event Marker
#Clear Keyframe Callback
replaymod.gui.clearcallback.title=Clear all Keyframes?
replaymod.gui.clearcallback.message=This Callback can be disabled in the Replay Settings.
#Asset Manager Gui
replaymod.gui.assets.title=Asset Manager
replaymod.gui.assets.filechooser=Asset File
replaymod.gui.assets.defaultname=New Asset
replaymod.gui.assets.emptylist=No Assets available
replaymod.gui.assets.namehint=Asset Name
replaymod.gui.assets.changefile=Change Asset File
replaymod.gui.assets.noselection=No Asset selected
#Object Manager Gui
replaymod.gui.objects.properties.anchor=Anchor Point
replaymod.gui.objects.properties.position=Position
replaymod.gui.objects.properties.scale=Scale
replaymod.gui.objects.properties.orientation=Orientation
replaymod.gui.objects.properties.opacity=Opacity
replaymod.gui.objects.properties.name=Object Name
replaymod.gui.objects.empty=No Objects added
replaymod.gui.objects.defaultname=New Object
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
replaymod.gui.modwarning.message1=A possible incompatibility between your current
replaymod.gui.modwarning.message2=Minecraft version and this Replay has been detected.
replaymod.gui.modwarning.message3=Loading this Replay may result in errors or a crash.
replaymod.gui.modwarning.name=Mod Name:
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
#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.

Some files were not shown because too many files have changed in this diff Show More