Compare commits

...

11 Commits

Author SHA1 Message Date
johni0702
5100f36b2c Merge branch '1.8' into 1.9.4
c93a3e3 Open Replay on double click (fixes #9)
8bb5d11 Update ReplayStudio
f3142a7 Pause replay after path playback (fixes #5)
3abaa1b Cleanup deleted corrupted replays after two days (fixes #1)
4b47f2c Add compatibility checking to ReplayViewer and ReplayCenter (fixes #7)
ada460c Deselect keyframe when left clicking empty space on timeline (fixes #6)
ad62060 Apply render settings only when their checkbox is enabled (fixes #3)
82246a5 Deselect Keyframe when timeline is changed (fixes #2)
2016-09-24 11:14:30 +02:00
johni0702
7ef6a6d166 Fix several sounds and the block break effect not being recorded (fixes #8) 2016-09-24 11:04:15 +02:00
johni0702
c93a3e384c Open Replay on double click (fixes #9) 2016-09-23 21:51:22 +02:00
johni0702
8bb5d11cb4 Update ReplayStudio
Fix wrap around of interpolated property parts (fixes #4)
2016-09-20 13:38:37 +02:00
johni0702
f3142a714c Pause replay after path playback (fixes #5) 2016-09-19 18:01:01 +02:00
johni0702
3abaa1bcbb Cleanup deleted corrupted replays after two days (fixes #1) 2016-09-19 17:49:30 +02:00
johni0702
4b47f2cbf8 Add compatibility checking to ReplayViewer and ReplayCenter (fixes #7) 2016-09-19 17:24:47 +02:00
johni0702
446b9be384 Revert ReplayCenter compatibility checking from "Update to Minecraft 1.9.4"
This reverts part of commit a80a20c37a.
2016-09-19 15:29:42 +02:00
johni0702
ada460c384 Deselect keyframe when left clicking empty space on timeline (fixes #6) 2016-09-18 13:20:28 +02:00
johni0702
ad62060368 Apply render settings only when their checkbox is enabled (fixes #3) 2016-09-18 12:53:29 +02:00
johni0702
82246a5f48 Deselect Keyframe when timeline is changed (fixes #2) 2016-09-18 11:37:36 +02:00
12 changed files with 164 additions and 16 deletions

2
jGui

Submodule jGui updated: f7270bb5f9...87f7f27009

View File

@@ -122,6 +122,22 @@ public class ReplayMod {
testIfMoeshAndExitMinecraft(); testIfMoeshAndExitMinecraft();
runLater(() -> { runLater(() -> {
// Cleanup deleted corrupted replays
try {
File[] files = getReplayFolder().listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory() && file.getName().endsWith(".mcpr.del")) {
if (file.lastModified() + 2 * 24 * 60 * 60 * 1000 < System.currentTimeMillis()) {
FileUtils.deleteDirectory(file);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
// Restore corrupted replays // Restore corrupted replays
try { try {
File[] files = getReplayFolder().listFiles(); File[] files = getReplayFolder().listFiles();

View File

@@ -79,6 +79,7 @@ public abstract class AbstractTimelinePlayer {
public void onTick(ReplayTimer.UpdatedEvent event) { public void onTick(ReplayTimer.UpdatedEvent event) {
if (future.isDone()) { if (future.isDone()) {
mc.timer = ((ReplayTimer) mc.timer).getWrapped(); mc.timer = ((ReplayTimer) mc.timer).getWrapped();
replayHandler.getReplaySender().setReplaySpeed(0);
replayHandler.getReplaySender().setAsyncMode(true); replayHandler.getReplaySender().setAsyncMode(true);
MinecraftForge.EVENT_BUS.unregister(this); MinecraftForge.EVENT_BUS.unregister(this);
return; return;

View File

@@ -11,6 +11,8 @@ import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.network.play.server.*; import net.minecraft.network.play.server.*;
import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos; 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;
@@ -69,6 +71,25 @@ public class RecordingEventHandler {
} }
} }
public void onClientSound(SoundEvent sound, SoundCategory category,
double x, double y, double z, float volume, float pitch) {
try {
// Send to all other players in ServerWorldEventHandler#playSoundToAllNearExcept
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) {
try {
// Send to all other players in ServerWorldEventHandler#playEvent
packetListener.save(new SPacketEffect(type, pos, data, false));
} catch(Exception e) {
e.printStackTrace();
}
}
@SubscribeEvent @SubscribeEvent
public void onPlayerTick(PlayerTickEvent e) { public void onPlayerTick(PlayerTickEvent e) {
try { try {

View File

@@ -0,0 +1,28 @@
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

@@ -0,0 +1,62 @@
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

@@ -186,7 +186,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
// Closing this GUI ensures that settings are saved // Closing this GUI ensures that settings are saved
getMinecraft().displayGuiScreen(null); getMinecraft().displayGuiScreen(null);
try { try {
VideoRenderer videoRenderer = new VideoRenderer(save(true), replayHandler, timeline); VideoRenderer videoRenderer = new VideoRenderer(save(false), replayHandler, timeline);
videoRenderer.renderVideo(); videoRenderer.renderVideo();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -378,7 +378,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
updateInputs(); updateInputs();
} }
public RenderSettings save(boolean saveOutputFile) { public RenderSettings save(boolean serialize) {
return new RenderSettings( return new RenderSettings(
renderMethodDropdown.getSelectedValue(), renderMethodDropdown.getSelectedValue(),
encodingPresetDropdown.getSelectedValue(), encodingPresetDropdown.getSelectedValue(),
@@ -386,13 +386,13 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
videoHeight.getInteger(), videoHeight.getInteger(),
frameRateSlider.getValue() + 10, frameRateSlider.getValue() + 10,
bitRateField.getInteger() << (10 * bitRateUnit.getSelected()), bitRateField.getInteger() << (10 * bitRateUnit.getSelected()),
saveOutputFile ? outputFile : null, serialize ? null : outputFile,
nametagCheckbox.isChecked(), nametagCheckbox.isChecked(),
stabilizeYaw.isChecked(), stabilizeYaw.isChecked() && (serialize || stabilizeYaw.isEnabled()),
stabilizePitch.isChecked(), stabilizePitch.isChecked() && (serialize || stabilizePitch.isEnabled()),
stabilizeRoll.isChecked(), stabilizeRoll.isChecked() && (serialize || stabilizeRoll.isEnabled()),
chromaKeyingCheckbox.isChecked() ? chromaKeyingColor.getColor() : null, chromaKeyingCheckbox.isChecked() ? chromaKeyingColor.getColor() : null,
inject360Metadata.isChecked(), inject360Metadata.isChecked() && (serialize || inject360Metadata.isEnabled()),
exportCommand.getText(), exportCommand.getText(),
exportArguments.getText(), exportArguments.getText(),
net.minecraft.client.gui.GuiScreen.isCtrlKeyDown() net.minecraft.client.gui.GuiScreen.isCtrlKeyDown()
@@ -412,7 +412,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
@Override @Override
public void close() { public void close() {
RenderSettings settings = save(false); RenderSettings settings = save(true);
String json = new Gson().toJson(settings); String json = new Gson().toJson(settings);
Configuration config = ReplayModRender.instance.getConfiguration(); Configuration config = ReplayModRender.instance.getConfiguration();
getConfigProperty(config).set(json); getConfigProperty(config).set(json);

View File

@@ -6,7 +6,12 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.mojang.realmsclient.gui.ChatFormatting; import com.mojang.realmsclient.gui.ChatFormatting;
import com.replaymod.core.gui.GuiReplaySettings; import com.replaymod.core.gui.GuiReplaySettings;
import com.replaymod.core.utils.Utils;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.AbstractGuiContainer; import de.johni0702.minecraft.gui.container.AbstractGuiContainer;
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;
@@ -20,11 +25,6 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup; import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer; import de.johni0702.minecraft.gui.utils.Consumer;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import com.replaymod.core.utils.Utils;
import net.minecraft.client.gui.GuiErrorScreen; import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.util.Util; import net.minecraft.util.Util;
@@ -54,6 +54,9 @@ public class GuiReplayViewer extends GuiScreen {
@Override @Override
public void run() { public void run() {
replayButtonPanel.forEach(IGuiButton.class).setEnabled(list.getSelected() != null); replayButtonPanel.forEach(IGuiButton.class).setEnabled(list.getSelected() != null);
if (list.getSelected() != null && list.getSelected().incompatible) {
loadButton.setDisabled();
}
} }
}).onLoad(new Consumer<Consumer<Supplier<GuiReplayEntry>>> () { }).onLoad(new Consumer<Consumer<Supplier<GuiReplayEntry>>> () {
@Override @Override
@@ -95,6 +98,10 @@ public class GuiReplayViewer extends GuiScreen {
e.printStackTrace(); e.printStackTrace();
} }
} }
}).onSelectionDoubleClicked(() -> {
if (this.loadButton.isEnabled()) {
this.loadButton.onClick();
}
}).setDrawShadow(true).setDrawSlider(true); }).setDrawShadow(true).setDrawSlider(true);
public final GuiButton loadButton = new GuiButton().onClick(new Runnable() { public final GuiButton loadButton = new GuiButton().onClick(new Runnable() {
@@ -270,6 +277,7 @@ public class GuiReplayViewer extends GuiScreen {
public final GuiLabel date = new GuiLabel().setColor(Colors.LIGHT_GRAY); public final GuiLabel date = new GuiLabel().setColor(Colors.LIGHT_GRAY);
public final GuiPanel infoPanel = new GuiPanel(this).setLayout(new VerticalLayout().setSpacing(2)) public final GuiPanel infoPanel = new GuiPanel(this).setLayout(new VerticalLayout().setSpacing(2))
.addElements(null, name, server, date); .addElements(null, name, server, date);
public final GuiLabel version = new GuiLabel(this).setColor(Colors.RED);
public final GuiImage thumbnail; public final GuiImage thumbnail;
public final GuiLabel duration = new GuiLabel(); public final GuiLabel duration = new GuiLabel();
public final GuiPanel durationPanel = new GuiPanel().setBackgroundColor(Colors.HALF_TRANSPARENT) public final GuiPanel durationPanel = new GuiPanel().setBackgroundColor(Colors.HALF_TRANSPARENT)
@@ -287,6 +295,7 @@ public class GuiReplayViewer extends GuiScreen {
}); });
private final long dateMillis; private final long dateMillis;
private final boolean incompatible;
public GuiReplayEntry(File file, ReplayMetaData metaData, BufferedImage thumbImage) { public GuiReplayEntry(File file, ReplayMetaData metaData, BufferedImage thumbImage) {
this.file = file; this.file = file;
@@ -297,6 +306,10 @@ public class GuiReplayViewer extends GuiScreen {
} else { } else {
server.setText(metaData.getServerName()); server.setText(metaData.getServerName());
} }
incompatible = !new ReplayStudio().isCompatible(metaData.getFileFormatVersion());
if (incompatible) {
version.setText("Minecraft " + metaData.getMcVersion());
}
dateMillis = metaData.getDate(); dateMillis = metaData.getDate();
date.setText(new SimpleDateFormat().format(new Date(dateMillis))); date.setText(new SimpleDateFormat().format(new Date(dateMillis)));
if (thumbImage == null) { if (thumbImage == null) {
@@ -316,6 +329,7 @@ public class GuiReplayViewer extends GuiScreen {
y(durationPanel, height(thumbnail) - height(durationPanel)); y(durationPanel, height(thumbnail) - height(durationPanel));
pos(infoPanel, width(thumbnail) + 5, 0); pos(infoPanel, width(thumbnail) + 5, 0);
pos(version, width - width(version), 0);
} }
@Override @Override

View File

@@ -84,6 +84,9 @@ public class ReplayModSimplePathing implements PathingRegistry {
} }
public void setCurrentTimeline(Timeline currentTimeline) { public void setCurrentTimeline(Timeline currentTimeline) {
if (this.currentTimeline != currentTimeline) {
selectedKeyframe = null;
}
this.currentTimeline = currentTimeline; this.currentTimeline = currentTimeline;
} }

View File

@@ -208,6 +208,7 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline
// Clicked on timeline but not on any keyframe // Clicked on timeline but not on any keyframe
if (button == 0) { // Left click if (button == 0) { // Left click
setCursorPosition(time); setCursorPosition(time);
gui.getMod().setSelectedKeyframe(null);
} else if (button == 1) { // Right click } else if (button == 1) { // Right click
if (pathKeyframePair.getLeft() != null) { if (pathKeyframePair.getLeft() != null) {
// Apply the value of the clicked path at the clicked position // Apply the value of the clicked path at the clicked position

View File

@@ -5,7 +5,9 @@
"server": [], "server": [],
"client": [ "client": [
"MixinNetHandlerPlayClient", "MixinNetHandlerPlayClient",
"MixinRenderGlobal" "MixinPlayerControllerMP",
"MixinRenderGlobal",
"MixinWorldClient"
], ],
"compatibilityLevel": "JAVA_8", "compatibilityLevel": "JAVA_8",
"refmap": "mixins.replaymod.refmap.json" "refmap": "mixins.replaymod.refmap.json"