Compare commits

..

9 Commits

Author SHA1 Message Date
Jonas Herzig
2a47f579a9 Merge branch '1.10.2' into 1.11
96b2904 Merge branch '1.9.4' into 1.10.2
6c8c779 Merge branch '1.8.9' into 1.9.4
aa16e42 Add workaround for setupCIWorkspace not being sufficient
ed56673 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 21:18:22 +02:00
johni0702
0c36022b47 Merge branch '1.10.2' into 1.11
325d041 Merge branch '1.9.4' into 1.10.2
d9d57e2 Merge branch '1.8.9' into 1.9.4
24c58cb Merge branch '1.8' into 1.8.9
1ffeb01 Update jGui
c122d30 Merge branch '1.8-path-segments' into 1.8
27fd25a Fix crash when server responds with garbage to language query (fixes #51)
db6b0ff Fix / by 0 when two spectator keyframes are closer than 50ms (fixes #60)
d61d0ac Fix hand of invisible spectated player being visible (fixes #59)
f7460f9 Re-enable time path after path playback (fixes #55)
bdbffd5 Add Catmull-Rom spline interpolator to simplepathing
5cbfd28 Generate interpolator settings from InterpolatorType enum
6095aef Fix loading and storing of default interpolator with settings
3f52336 Revert "Fix AbstractMethodError caused by FG2.0 not supporting lambdas (fixes #58)"
c6293bc Update to FG2.2
c8126ed Merge '1.8' into 1.8.9
64898ce Fix NPE when saving recorded resource pack
70e3e54 Fix AbstractMethodError caused by FG2.0 not supporting lambdas (fixes #58)
3f2b3d6 Fix replacing source file in Replay Editor (fixes #57)
d2def94 Fix duration of replays corrupted because of JVM/OS crash
2c64030 Refactor simple pathing timeline logic into dedicated class with unit tests Fix logic for user-set interpolators
4deb374 Enable validation on focus change for all number input fields
5302bca Fix id and text of Replay Editor button in docs
057edcc Manually inject root cert for cross-signed LetsEncrypt cert in requests
cd16211 Register new Property to imply that the path segment following a keyframe has a user-set interpolator instead of the default Modified Keyframe GUI to allow for custom Path interpolator
2017-01-29 13:25:19 +01:00
johni0702
b6cf42c3be Merge branch '1.10.2' into 1.11
c6dec95 Merge branch '1.9.4' into 1.10.2
4fdfa9e Merge branch '1.8.9' into 1.9.4
44d4a25 Merge branch '1.8' into 1.9.4
01acbc6 Update to 1.8.9
1c60687 Update ReplayStudio and jGui
d9b796d Mixin'd the Shader's mods ShadersRender class to only render hands if the Forge event isn't cancelled
6bbff3b Updated Mixin to 0.6.4-SNAPSHOT and set mixin target level
6f3ac71 Add CustomMainMenu compatibility info to docs
b0a1cdf Search for ffmpeg executable in common locations to simplify setup
d465891 Fixed GuiEditPositionKeyframe's width to fit on all screen sizes Centered all rows of GuiEditPositionKeyframe's VerticalLayout
1b099f9 Add render queue
03534c6 Update docs
4388d69 Only try to create the downloads folder if it does not yet exist
7ad19aa Add mod version and accepted MC version directly to the @Mod annotation
655bea0 Replace @Mod.Instance with direct static references
458dad6 Merge pull request #4 from ReplayMod/1.8-editor
eb5ccb2 Add Replay Editor (for now, trimming only)
6498d62 Fix server game data snapshot being restored in singleplayer (fixes #44)
2c898a8 Prevent rendering without sufficient keyframes (fixes #45)
5d520a5 Fix deserialization of chroma key color (fixes #43)
c681742 Use caching maven proxy for faster and more stable drone builds
fbf165e Added Anti-Aliasing to the Render Settings
2017-01-15 00:20:20 +01:00
johni0702
503b308043 Replace @Mod.Instance with direct static references
(cherry picked from commit 4b605cc)
2017-01-13 17:02:18 +01:00
CrushedPixel
7b491d188d Enabled recording on Realms Servers 2016-12-20 15:00:35 +01:00
johni0702
fb4140a123 Merge branch '1.10.2' into 1.11
905ae75 Merge branch '1.9.4' into 1.10.2
72e1fe0 Merge branch '1.8' into 1.9.4
6163585 Update ReplayStudio to fix dependencies
2016-12-16 22:09:16 +01:00
johni0702
2428c01c9c Merge branch '1.10.2' into 1.11
372206f Merge branch '1.9.4' into 1.10.2
4a1ccea Merge branch '1.8' into 1.9.4
fb39a06 Add drone ci config
030c71c Automatically disable and re-enable Optifine's Fast Render setting before rendering a video
1fef12d Include FML handshake in recording
e86c66d Move README.md, LICENSE.md and user docs from master branch to 1.8
2016-12-10 20:28:23 +01:00
johni0702
260720d5ba Update Forge to latest recommended build (2188) 2016-12-10 20:25:33 +01:00
johni0702
58b7c44a33 Update to 1.11 2016-12-03 21:12:08 +01:00
51 changed files with 167 additions and 696 deletions

View File

@@ -34,13 +34,11 @@ archivesBaseName = "replaymod"
minecraft { minecraft {
coreMod = 'com.replaymod.core.LoadingPlugin' coreMod = 'com.replaymod.core.LoadingPlugin'
version = '1.10.2-12.18.2.2099' version = '1.11-13.19.1.2188'
runDir = "eclipse" runDir = "eclipse"
mappings = "snapshot_20160518" mappings = "snapshot_20161111"
replace '@MOD_VERSION@', project.version replace '@MOD_VERSION@', project.version
// Includes intentional whitespace to stop Forge from declaring the mod to be compatible with replace '@MC_VERSION@', project.minecraft.version
// a newer srg-compatible MC version (that may be using a different protocol version)
replace '@MC_VERSION@', "[ ${project.minecraft.version} ]"
} }
repositories { repositories {

View File

@@ -1,8 +1,6 @@
package com.replaymod.compat; package com.replaymod.compat;
import com.replaymod.compat.bettersprinting.DisableBetterSprinting;
import com.replaymod.compat.optifine.DisableFastRender; import com.replaymod.compat.optifine.DisableFastRender;
import com.replaymod.compat.oranges17animations.HideInvisibleEntities;
import com.replaymod.compat.shaders.ShaderBeginRender; import com.replaymod.compat.shaders.ShaderBeginRender;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@@ -12,7 +10,6 @@ import net.minecraftforge.fml.common.eventhandler.EventBus;
@Mod(modid = ReplayModCompat.MOD_ID, @Mod(modid = ReplayModCompat.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModCompat { public class ReplayModCompat {
public static final String MOD_ID = "replaymod-compat"; public static final String MOD_ID = "replaymod-compat";
@@ -22,8 +19,6 @@ public class ReplayModCompat {
EventBus bus = MinecraftForge.EVENT_BUS; EventBus bus = MinecraftForge.EVENT_BUS;
bus.register(new ShaderBeginRender()); bus.register(new ShaderBeginRender());
bus.register(new DisableFastRender()); bus.register(new DisableFastRender());
bus.register(new HideInvisibleEntities());
DisableBetterSprinting.register();
} }
} }

View File

@@ -1,101 +0,0 @@
package com.replaymod.compat.bettersprinting;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.events.ReplayChatMessageEvent;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorldEventListener;
import net.minecraft.world.World;
import net.minecraftforge.client.event.GuiOpenEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion;
import net.minecraftforge.fml.common.versioning.Restriction;
import net.minecraftforge.fml.common.versioning.VersionRange;
import javax.annotation.Nullable;
import java.util.Collections;
/**
* Old Better Sprinting versions replace the vanilla player with their own, overridden instance (replacing the camera entity).
*
* See: https://github.com/chylex/Better-Sprinting/blob/1.8/src/main/java/chylex/bettersprinting/client/player/impl/LogicImplOverride.java
*/
public class DisableBetterSprinting {
private static final VersionRange OLD_VERSION = VersionRange.newRange(null,
Collections.singletonList(new Restriction(null, false, new DefaultArtifactVersion("2.0.0"), false)));
private static final String LOGIC_CLASS_NAME = "chylex.bettersprinting.client.player.impl.LogicImplOverride";
private static final String CONTROLLER_OVERRIDE_CLASS_NAME = LOGIC_CLASS_NAME + ".PlayerControllerMPOverride";
public static void register() {
Loader.instance().getModList().stream()
.filter(mod -> mod.getModId().equalsIgnoreCase("bettersprinting"))
.findFirst()
.map(ModContainer::getProcessedVersion).filter(OLD_VERSION::containsVersion)
.ifPresent($_ -> MinecraftForge.EVENT_BUS.register(new DisableBetterSprinting()));
}
private DisableBetterSprinting() {}
private final Minecraft mc = Minecraft.getMinecraft();
private PlayerControllerMP originalController;
private BetterSprintingWorldAccess worldAccessHook = new BetterSprintingWorldAccess();
@SubscribeEvent(priority = EventPriority.HIGH)
public void beforeGuiOpenEvent(GuiOpenEvent event) {
if (ReplayModReplay.instance.getReplayHandler() != null && mc.theWorld != null) {
// During replay, get ready to revert BetterSprinting's overwritten playerController
originalController = mc.playerController;
mc.theWorld.addEventListener(worldAccessHook);
}
}
@SubscribeEvent(priority = EventPriority.LOW)
public void afterGuiOpenEvent(GuiOpenEvent event) {
if (ReplayModReplay.instance.getReplayHandler() != null && mc.theWorld != null) {
mc.theWorld.addEventListener(worldAccessHook);
}
}
@SubscribeEvent
public void onReplayChatMessage(ReplayChatMessageEvent event) {
// Suppress this message if it's the Better Sprinting warning message
for (StackTraceElement elem : Thread.currentThread().getStackTrace()) {
if (LOGIC_CLASS_NAME.equals(elem.getClassName())) {
event.setCanceled(true);
return;
}
}
}
private class BetterSprintingWorldAccess implements IWorldEventListener {
@Override
public void onEntityRemoved(Entity entityIn) {
if (mc.playerController != null && mc.playerController.getClass().getName().equals(CONTROLLER_OVERRIDE_CLASS_NAME)) {
// Someone has secretly swapped out the player controller and is about to substitute their own player entity.
// This is the right time to destroy their plan.
mc.playerController = originalController;
}
}
@Override public void notifyBlockUpdate(World worldIn, BlockPos pos, IBlockState oldState, IBlockState newState, int flags) {}
@Override public void notifyLightSet(BlockPos pos) {}
@Override public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2) {}
@Override public void playSoundToAllNearExcept(@Nullable EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch) {}
@Override public void playRecord(SoundEvent soundIn, BlockPos pos) {}
@Override public void spawnParticle(int p_180442_1_, boolean p_180442_2_, double p_180442_3_, double p_180442_5_, double p_180442_7_, double p_180442_9_, double p_180442_11_, double p_180442_13_, int... p_180442_15_) {}
@Override public void onEntityAdded(Entity entityIn) {}
@Override public void broadcastSound(int p_180440_1_, BlockPos p_180440_2_, int p_180440_3_) {}
@Override public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data) {}
@Override public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {}
}
}

View File

@@ -1,28 +0,0 @@
package com.replaymod.compat.oranges17animations;
import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.client.Minecraft;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
/**
* Orange seems to have copied vast parts of the RendererLivingEntity into their ArmorAnimation class which cancels the RenderLivingEvent.Pre and calls its own code instead.
* This breaks our mixin which assures that, even though the camera is in spectator mode, it cannot see invisible entities.
*
* To fix this issue, we simply cancel the RenderLivingEvent.Pre before it gets to ArmorAnimation if the entity is invisible.
*/
public class HideInvisibleEntities {
private final Minecraft mc = Minecraft.getMinecraft();
private final boolean modLoaded = Loader.isModLoaded("animations");
@SubscribeEvent(priority = EventPriority.HIGH)
public void preRenderLiving(RenderLivingEvent.Pre event) {
if (modLoaded) {
if (mc.thePlayer instanceof CameraEntity && event.getEntity().isInvisible()) {
event.setCanceled(true);
}
}
}
}

View File

@@ -20,7 +20,6 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
Mixins.addConfiguration("mixins.render.replaymod.json"); Mixins.addConfiguration("mixins.render.replaymod.json");
Mixins.addConfiguration("mixins.replay.replaymod.json"); Mixins.addConfiguration("mixins.replay.replaymod.json");
Mixins.addConfiguration("mixins.compat.shaders.replaymod.json"); Mixins.addConfiguration("mixins.compat.shaders.replaymod.json");
Mixins.addConfiguration("mixins.extras.playeroverview.replaymod.json");
CodeSource codeSource = getClass().getProtectionDomain().getCodeSource(); CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
if (codeSource != null) { if (codeSource != null) {

View File

@@ -26,9 +26,6 @@ import net.minecraftforge.fml.common.ModContainer;
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;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus;
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 java.io.File; import java.io.File;
@@ -39,7 +36,6 @@ import java.util.List;
useMetadata = true, useMetadata = true,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json", updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
guiFactory = "com.replaymod.core.gui.GuiFactory") guiFactory = "com.replaymod.core.gui.GuiFactory")
public class ReplayMod { public class ReplayMod {
@@ -163,36 +159,9 @@ public class ReplayMod {
}); });
} }
/**
* Set when the currently running code has been scheduled by runLater.
* If this is the case, subsequent calls to runLater have to be delayed until all scheduled tasks have been
* processed, otherwise a livelock may occur.
*/
private boolean inRunLater = false;
public void runLater(Runnable runnable) { public void runLater(Runnable runnable) {
if (mc.isCallingFromMinecraftThread() && inRunLater) {
EventBus bus = MinecraftForge.EVENT_BUS;
bus.register(new Object() {
@SubscribeEvent
public void onRenderTick(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
runLater(runnable);
bus.unregister(this);
}
}
});
return;
}
synchronized (mc.scheduledTasks) { synchronized (mc.scheduledTasks) {
mc.scheduledTasks.add(ListenableFutureTask.create(() -> { mc.scheduledTasks.add(ListenableFutureTask.create(runnable, null));
inRunLater = true;
try {
runnable.run();
} finally {
inRunLater = false;
}
}, null));
} }
} }

View File

@@ -1,6 +1,5 @@
package com.replaymod.core.utils; package com.replaymod.core.utils;
import com.google.common.net.PercentEscaper;
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;
@@ -17,14 +16,11 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiInfoPopup; import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import lombok.SneakyThrows;
import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.network.NetworkPlayerInfo; import net.minecraft.client.network.NetworkPlayerInfo;
import net.minecraft.client.resources.DefaultPlayerSkin; import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
@@ -40,8 +36,6 @@ import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
@@ -143,22 +137,6 @@ public class Utils {
return mail.matches("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$"); return mail.matches("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$");
} }
private static final PercentEscaper REPLAY_NAME_ENCODER = new PercentEscaper(".-_ ", false);
public static String replayNameToFileName(String replayName) {
return REPLAY_NAME_ENCODER.escape(replayName) + ".mcpr";
}
@SneakyThrows(UnsupportedEncodingException.class)
public static String fileNameToReplayName(String fileName) {
String baseName = FilenameUtils.getBaseName(fileName);
try {
return URLDecoder.decode(baseName, Charsets.UTF_8.name());
} catch (IllegalArgumentException e) {
return baseName;
}
}
public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) { public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) {
NetworkPlayerInfo info = getMinecraft().getConnection().getPlayerInfo(uuid); NetworkPlayerInfo info = getMinecraft().getConnection().getPlayerInfo(uuid);
ResourceLocation skinLocation; ResourceLocation skinLocation;

View File

@@ -11,7 +11,6 @@ import org.apache.logging.log4j.Logger;
@Mod(modid = ReplayModEditor.MOD_ID, @Mod(modid = ReplayModEditor.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModEditor { public class ReplayModEditor {
public static final String MOD_ID = "replaymod-editor"; public static final String MOD_ID = "replaymod-editor";

View File

@@ -36,6 +36,7 @@ import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -124,7 +125,7 @@ public class GuiReplayEditor extends GuiScreen {
} }
public void save(File inputFile, PacketStream.FilterInfo...filters) { public void save(File inputFile, PacketStream.FilterInfo...filters) {
save(Utils.fileNameToReplayName(inputFile.getName()), (outputFile) -> { save(FilenameUtils.getBaseName(inputFile.getName()), (outputFile) -> {
Studio studio = new ReplayStudio(); Studio studio = new ReplayStudio();
File tmpDir = null; File tmpDir = null;
try { try {
@@ -219,7 +220,7 @@ public class GuiReplayEditor extends GuiScreen {
Utils.error(LOGGER, GuiReplayEditor.this, CrashReport.makeCrashReport(e, "Getting replay folder"), null); Utils.error(LOGGER, GuiReplayEditor.this, CrashReport.makeCrashReport(e, "Getting replay folder"), null);
return; return;
} }
File targetFile = new File(replayFolder, Utils.replayNameToFileName(name)); File targetFile = new File(replayFolder, name + ".mcpr");
if (targetFile.exists()) { if (targetFile.exists()) {
LOGGER.trace("Selected file already exists, asking for confirmation"); LOGGER.trace("Selected file already exists, asking for confirmation");
Futures.addCallback(GuiYesNoPopup.open(GuiReplayEditor.this, Futures.addCallback(GuiYesNoPopup.open(GuiReplayEditor.this,

View File

@@ -20,6 +20,7 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOCase; import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.SuffixFileFilter; import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
@@ -51,7 +52,7 @@ public class GuiTrimPanel extends GuiPanel {
public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this) public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this)
.setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay()) .setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay())
.setToString(f -> f == NO_REPLAY ? "" : Utils.fileNameToReplayName(f.getName())); .setToString(f -> f == NO_REPLAY ? "" : FilenameUtils.getBaseName(f.getName()));
public final GuiNumberField startHour = newGuiNumberField(); public final GuiNumberField startHour = newGuiNumberField();
public final GuiNumberField startMin = newGuiNumberField(); public final GuiNumberField startMin = newGuiNumberField();

View File

@@ -10,15 +10,11 @@ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
@Mod(modid = ReplayModExtras.MOD_ID, @Mod(modid = ReplayModExtras.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModExtras { public class ReplayModExtras {
public static final String MOD_ID = "replaymod-extras"; public static final String MOD_ID = "replaymod-extras";
@@ -36,8 +32,6 @@ public class ReplayModExtras {
OpenEyeExtra.class OpenEyeExtra.class
); );
private final Map<Class<? extends Extra>, Extra> instances = new HashMap<>();
public static Logger LOGGER; public static Logger LOGGER;
@Mod.EventHandler @Mod.EventHandler
@@ -51,14 +45,9 @@ public class ReplayModExtras {
try { try {
Extra extra = cls.newInstance(); Extra extra = cls.newInstance();
extra.register(ReplayMod.instance); extra.register(ReplayMod.instance);
instances.put(cls, extra);
} catch (Throwable t) { } catch (Throwable t) {
LOGGER.warn("Failed to load extra " + cls.getName() + ": ", t); LOGGER.warn("Failed to load extra " + cls.getName() + ": ", t);
} }
} }
} }
public <T extends Extra> Optional<T> get(Class<T> cls) {
return Optional.ofNullable(instances.get(cls)).map(cls::cast);
}
} }

View File

@@ -9,6 +9,8 @@ import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.events.ReplayCloseEvent; import com.replaymod.replay.events.ReplayCloseEvent;
import com.replaymod.replay.events.ReplayOpenEvent; import com.replaymod.replay.events.ReplayOpenEvent;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.entity.Entity; 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;
@@ -56,6 +58,12 @@ public class PlayerOverview implements Extra {
}); });
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
RenderManager renderManager = mod.getMinecraft().getRenderManager();
@SuppressWarnings("unchecked")
Map<String, RenderPlayer> skinMap = renderManager.skinMap;
skinMap.put("default", new PlayerRenderHook(this, renderManager, false));
skinMap.put("slim", new PlayerRenderHook(this, renderManager, true));
} }
public boolean isHidden(UUID uuid) { public boolean isHidden(UUID uuid) {

View File

@@ -0,0 +1,20 @@
package com.replaymod.extras.playeroverview;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.entity.RenderPlayer;
public class PlayerRenderHook extends RenderPlayer {
private final PlayerOverview extra;
public PlayerRenderHook(PlayerOverview extra, RenderManager renderManager, boolean useSmallArms) {
super(renderManager, useSmallArms);
this.extra = extra;
}
@Override
public boolean shouldRender(AbstractClientPlayer entity, ICamera camera, double camX, double camY, double camZ) {
return !extra.isHidden(entity.getUniqueID()) && super.shouldRender(entity, camera, camX, camY, camZ);
}
}

View File

@@ -1,40 +0,0 @@
package com.replaymod.extras.playeroverview.mixin;
import com.replaymod.extras.ReplayModExtras;
import com.replaymod.extras.playeroverview.PlayerOverview;
import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
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;
/**
* This mixin prevents players that are hidden in the PlayerOverview from being rendered.
*
* Cancelling the RenderPlayerEvent.Pre is insufficient because it affects neither the shadows nor the fire texture.
* See: https://github.com/MinecraftForge/MinecraftForge/issues/2987
*
* The previous solution was to overwrite the RenderPlayer instances which has been dropped in favor of this one
* because it is less compatible with other mods whereas this one should be fine as long as no other mod completely
* overwrites the shouldRender method.
* One example of the previous solution breaking is when used with VanillaEnhancements because it replaces the
* RenderManager with a new custom one which in turn will reset our registered RenderPlayer instances because
* it does so after we have already registered with the old RenderManager.
*/
@Mixin(value = Render.class, priority = 1200)
public abstract class MixinRender {
@Inject(method = "shouldRender", at=@At("HEAD"), cancellable = true)
public void replayModExtras_isPlayerHidden(Entity entity, ICamera camera, double camX, double camY, double camZ, CallbackInfoReturnable<Boolean> ci) {
ReplayModExtras.instance.get(PlayerOverview.class).ifPresent(playerOverview -> {
if (entity instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) entity;
if (playerOverview.isHidden(player.getUniqueID())) {
ci.setReturnValue(false);
}
}
});
}
}

View File

@@ -25,7 +25,6 @@ public class LinuxUriScheme extends UriScheme {
"Icon=" + iconFile.getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"") + "\n" + "Icon=" + iconFile.getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"") + "\n" +
"Type=Application\n" + "Type=Application\n" +
"Terminal=false\n" + "Terminal=false\n" +
"NoDisplay=true\n" +
"MimeType=x-scheme-handler/replaymod;"; "MimeType=x-scheme-handler/replaymod;";
FileOutputStream out = new FileOutputStream(file); FileOutputStream out = new FileOutputStream(file);

View File

@@ -36,8 +36,6 @@ import java.security.GeneralSecurityException;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class YoutubeUploader { public class YoutubeUploader {
private static final String CLIENT_ID = "743126594724-mfe7pj1k7e47uu5pk4503c8st9vj9ibu.apps.googleusercontent.com"; private static final String CLIENT_ID = "743126594724-mfe7pj1k7e47uu5pk4503c8st9vj9ibu.apps.googleusercontent.com";
private static final String CLIENT_SECRET = "gMwcy3mRYCRamCIjJIYP7rqc"; private static final String CLIENT_SECRET = "gMwcy3mRYCRamCIjJIYP7rqc";
@@ -139,7 +137,7 @@ public class YoutubeUploader {
CommandLine commandLine = new CommandLine(settings.getExportCommand()); CommandLine commandLine = new CommandLine(settings.getExportCommand());
commandLine.addArguments(args); commandLine.addArguments(args);
LOGGER.info("Re-encoding for ODS with {} {}", settings.getExportCommand(), args); System.out.println("Re-encoding for ODS with " + settings.getExportCommand() + args);
Process process = new ProcessBuilder(commandLine.toStrings()).directory(videoFile.getParentFile()).start(); Process process = new ProcessBuilder(commandLine.toStrings()).directory(videoFile.getParentFile()).start();
final AtomicBoolean active = new AtomicBoolean(true); final AtomicBoolean active = new AtomicBoolean(true);
@@ -151,7 +149,7 @@ public class YoutubeUploader {
char c = (char) in.read(); char c = (char) in.read();
if (c == '\r') { if (c == '\r') {
String str = sb.toString(); String str = sb.toString();
LOGGER.debug("[FFmpeg] {}", str); System.out.println(str);
if (str.startsWith("frame=")) { if (str.startsWith("frame=")) {
str = str.substring(6).trim(); str = str.substring(6).trim();
str = str.substring(0, str.indexOf(' ')); str = str.substring(0, str.indexOf(' '));

View File

@@ -29,7 +29,6 @@ import static net.minecraft.client.Minecraft.getMinecraft;
@Mod(modid = ReplayModOnline.MOD_ID, @Mod(modid = ReplayModOnline.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModOnline { public class ReplayModOnline {
public static final String MOD_ID = "replaymod-online"; public static final String MOD_ID = "replaymod-online";
@@ -41,7 +40,7 @@ public class ReplayModOnline {
private ReplayModReplay replayModule; private ReplayModReplay replayModule;
public static Logger LOGGER; private Logger logger;
private ApiClient apiClient; private ApiClient apiClient;
@@ -54,7 +53,7 @@ public class ReplayModOnline {
@Mod.EventHandler @Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) { public void preInit(FMLPreInitializationEvent event) {
LOGGER = event.getModLog(); logger = event.getModLog();
core = ReplayMod.instance; core = ReplayMod.instance;
replayModule = ReplayModReplay.instance; replayModule = ReplayModReplay.instance;
@@ -70,7 +69,7 @@ public class ReplayModOnline {
public void init(FMLInitializationEvent event) { public void init(FMLInitializationEvent event) {
if (!getDownloadsFolder().exists()){ if (!getDownloadsFolder().exists()){
if (!getDownloadsFolder().mkdirs()) { if (!getDownloadsFolder().mkdirs()) {
LOGGER.warn("Failed to create downloads folder: " + getDownloadsFolder()); logger.warn("Failed to create downloads folder: " + getDownloadsFolder());
} }
} }
@@ -83,10 +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()) {
core.runLater(() -> { core.runLater(() -> new GuiLoginPrompt(apiClient, GuiScreen.wrap(getMinecraft().currentScreen), null, false).display());
GuiScreen parent = GuiScreen.wrap(getMinecraft().currentScreen);
new GuiLoginPrompt(apiClient, parent, parent, false).display();
});
} }
} }
} }
@@ -100,7 +96,7 @@ public class ReplayModOnline {
} }
public Logger getLogger() { public Logger getLogger() {
return LOGGER; return logger;
} }
public ApiClient getApiClient() { public ApiClient getApiClient() {

View File

@@ -4,7 +4,8 @@ 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_9_4("Minecraft 1.9.4", "1.9.4"),
MC_1_10_2("Minecraft 1.10.2", "1.10.2"); MC_1_10_2("Minecraft 1.10.2", "1.10.2"),
MC_1_11("Minecraft 1.11", "1.11");
private String niceName, apiName; private String niceName, apiName;

View File

@@ -37,6 +37,8 @@ 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 net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.core.helpers.Strings; import org.apache.logging.log4j.core.helpers.Strings;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
@@ -49,8 +51,6 @@ import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import static com.replaymod.online.ReplayModOnline.LOGGER;
public class GuiReplayCenter extends GuiScreen { public class GuiReplayCenter extends GuiScreen {
private final ReplayModOnline mod; private final ReplayModOnline mod;
private final ApiClient apiClient; private final ApiClient apiClient;
@@ -336,7 +336,7 @@ public class GuiReplayCenter extends GuiScreen {
} }
}); });
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("Could not load Replay File {}", fileInfo.getId(), e); FMLLog.getLogger().error("Could not load Replay File " + fileInfo.getId(), e);
} }
} }
} }
@@ -417,7 +417,7 @@ public class GuiReplayCenter extends GuiScreen {
this.downloaded = downloaded; this.downloaded = downloaded;
ReplayMetaData metaData = fileInfo.getMetadata(); ReplayMetaData metaData = fileInfo.getMetadata();
name.setText(ChatFormatting.UNDERLINE + Utils.fileNameToReplayName(fileInfo.getName())); name.setText(ChatFormatting.UNDERLINE + FilenameUtils.getBaseName(fileInfo.getName()));
author.setI18nText("replaymod.gui.center.author", author.setI18nText("replaymod.gui.center.author",
"" + ChatFormatting.GRAY + ChatFormatting.ITALIC, fileInfo.getOwner()); "" + ChatFormatting.GRAY + ChatFormatting.ITALIC, fileInfo.getOwner());
if (Strings.isEmpty(metaData.getServerName())) { if (Strings.isEmpty(metaData.getServerName())) {

View File

@@ -2,7 +2,6 @@ package com.replaymod.online.gui;
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.replaymod.core.utils.Utils;
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.GuiButton; import de.johni0702.minecraft.gui.element.GuiButton;
@@ -28,7 +27,7 @@ public class GuiSaveModifiedReplay extends GuiScreen {
@Override @Override
public void run() { public void run() {
String resultName = name.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_"); String resultName = name.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_");
final File resultFile = new File(file.getParentFile(), Utils.replayNameToFileName(resultName)); final File resultFile = new File(file.getParentFile(), resultName + ".mcpr");
if (resultFile.exists()) { if (resultFile.exists()) {
Futures.addCallback(GuiYesNoPopup.open(GuiSaveModifiedReplay.this, Futures.addCallback(GuiYesNoPopup.open(GuiSaveModifiedReplay.this,
new GuiLabel().setI18nText("replaymod.gui.replaymodified.warning1", resultName).setColor(Colors.BLACK), new GuiLabel().setI18nText("replaymod.gui.replaymodified.warning1", resultName).setColor(Colors.BLACK),

View File

@@ -38,6 +38,7 @@ import net.minecraft.client.settings.KeyBinding;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException; import net.minecraft.util.ReportedException;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -134,7 +135,7 @@ public class GuiUploadReplay extends GuiScreen {
} }
// Apply to gui // Apply to gui
name.setText(Utils.fileNameToReplayName(file.getName())); name.setText(FilenameUtils.getBaseName(file.getName()));
int secs = metaData.getDuration() / 1000; int secs = metaData.getDuration() / 1000;
durationLabel.setI18nText("replaymod.gui.upload.duration", secs / 60, secs % 60); durationLabel.setI18nText("replaymod.gui.upload.duration", secs / 60, secs % 60);
hideServerIP.setEnabled(!metaData.isSingleplayer()); hideServerIP.setEnabled(!metaData.isSingleplayer());

View File

@@ -19,7 +19,6 @@ import org.lwjgl.input.Keyboard;
@Mod(modid = ReplayModRecording.MOD_ID, @Mod(modid = ReplayModRecording.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModRecording { public class ReplayModRecording {
public static final String MOD_ID = "replaymod-recording"; public static final String MOD_ID = "replaymod-recording";

View File

@@ -2,7 +2,6 @@ package com.replaymod.recording.handler;
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.core.utils.Utils;
import com.replaymod.recording.Setting; import com.replaymod.recording.Setting;
import com.replaymod.recording.gui.GuiRecordingOverlay; import com.replaymod.recording.gui.GuiRecordingOverlay;
import com.replaymod.recording.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
@@ -67,6 +66,9 @@ public class ConnectionEventHandler {
worldName = mc.getIntegratedServer().getWorldName(); worldName = mc.getIntegratedServer().getWorldName();
} else if (Minecraft.getMinecraft().getCurrentServerData() != null) { } else if (Minecraft.getMinecraft().getCurrentServerData() != null) {
worldName = Minecraft.getMinecraft().getCurrentServerData().serverIP; worldName = Minecraft.getMinecraft().getCurrentServerData().serverIP;
} else if (Minecraft.getMinecraft().isConnectedToRealms()) {
// we can't access the server name without tapping too deep in the Realms Library
worldName = "A Realms Server";
} else { } else {
logger.info("Recording not started as the world is neither local nor remote (probably a replay)."); logger.info("Recording not started as the world is neither local nor remote (probably a replay).");
return; return;
@@ -75,7 +77,7 @@ public class ConnectionEventHandler {
File folder = core.getReplayFolder(); File folder = core.getReplayFolder();
String name = sdf.format(Calendar.getInstance().getTime()); String name = sdf.format(Calendar.getInstance().getTime());
File currentFile = new File(folder, Utils.replayNameToFileName(name)); File currentFile = new File(folder, name + ".mcpr");
ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), currentFile); ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), currentFile);
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods()); replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());

View File

@@ -178,23 +178,23 @@ public class RecordingEventHandler {
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.OFFHAND, playerItems[1])); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.OFFHAND, playerItems[1]));
} }
if (playerItems[2] != mc.thePlayer.inventory.armorInventory[0]) { if (playerItems[2] != mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.FEET)) {
playerItems[2] = mc.thePlayer.inventory.armorInventory[0]; playerItems[2] = mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.FEET);
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.FEET, playerItems[2])); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.FEET, playerItems[2]));
} }
if (playerItems[3] != mc.thePlayer.inventory.armorInventory[1]) { if (playerItems[3] != mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.LEGS)) {
playerItems[3] = mc.thePlayer.inventory.armorInventory[1]; playerItems[3] = mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.LEGS);
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.LEGS, playerItems[3])); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.LEGS, playerItems[3]));
} }
if (playerItems[4] != mc.thePlayer.inventory.armorInventory[2]) { if (playerItems[4] != mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.CHEST)) {
playerItems[4] = mc.thePlayer.inventory.armorInventory[2]; playerItems[4] = mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.CHEST);
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.CHEST, playerItems[4])); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.CHEST, playerItems[4]));
} }
if (playerItems[5] != mc.thePlayer.inventory.armorInventory[3]) { if (playerItems[5] != mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.HEAD)) {
playerItems[5] = mc.thePlayer.inventory.armorInventory[3]; playerItems[5] = mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.HEAD);
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.HEAD, playerItems[5])); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.HEAD, playerItems[5]));
} }
@@ -234,7 +234,8 @@ 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 SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
event.pickedUp.getEntityItem().getMaxStackSize()));
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@@ -15,12 +15,7 @@ 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.datasync.EntityDataManager;
import net.minecraft.network.play.server.SPacketCollectItem; import net.minecraft.network.play.server.*;
import net.minecraft.network.play.server.SPacketCustomPayload;
import net.minecraft.network.play.server.SPacketDisconnect;
import net.minecraft.network.play.server.SPacketResourcePackSend;
import net.minecraft.network.play.server.SPacketSpawnMob;
import net.minecraft.network.play.server.SPacketSpawnPlayer;
import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextComponentString;
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;

View File

@@ -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 = ResourcePackRepository.getDownloadHeaders();
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

@@ -21,7 +21,6 @@ import java.util.List;
@Mod(modid = ReplayModRender.MOD_ID, @Mod(modid = ReplayModRender.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModRender { public class ReplayModRender {
public static final String MOD_ID = "replaymod-render"; public static final String MOD_ID = "replaymod-render";

View File

@@ -2,7 +2,6 @@ package com.replaymod.render;
import com.replaymod.render.frame.RGBFrame; import com.replaymod.render.frame.RGBFrame;
import com.replaymod.render.rendering.FrameConsumer; import com.replaymod.render.rendering.FrameConsumer;
import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.render.utils.ByteBufferPool; import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.StreamPipe; import com.replaymod.render.utils.StreamPipe;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@@ -33,7 +32,6 @@ import static org.apache.commons.lang3.Validate.isTrue;
public class VideoWriter implements FrameConsumer<RGBFrame> { public class VideoWriter implements FrameConsumer<RGBFrame> {
private final VideoRenderer renderer;
private final RenderSettings settings; private final RenderSettings settings;
private final Process process; private final Process process;
private final OutputStream outputStream; private final OutputStream outputStream;
@@ -43,9 +41,8 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
private ByteArrayOutputStream ffmpegLog = new ByteArrayOutputStream(4096); private ByteArrayOutputStream ffmpegLog = new ByteArrayOutputStream(4096);
public VideoWriter(final VideoRenderer renderer) throws IOException { public VideoWriter(final RenderSettings settings) throws IOException {
this.renderer = renderer; this.settings = settings;
this.settings = renderer.getRenderSettings();
File outputFolder = settings.getOutputFile().getParentFile(); File outputFolder = settings.getOutputFile().getParentFile();
FileUtils.forceMkdir(outputFolder); FileUtils.forceMkdir(outputFolder);
@@ -60,13 +57,9 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
.replace("%FILTERS%", settings.getVideoFilters()); .replace("%FILTERS%", settings.getVideoFilters());
String executable = settings.getExportCommand().isEmpty() ? findFFmpeg() : settings.getExportCommand(); String executable = settings.getExportCommand().isEmpty() ? findFFmpeg() : settings.getExportCommand();
LOGGER.info("Starting {} with args: {}", executable, commandArgs); System.out.println("Starting " + executable + " with args: " + commandArgs);
String[] cmdline = new CommandLine(executable).addArguments(commandArgs).toStrings(); String[] cmdline = new CommandLine(executable).addArguments(commandArgs).toStrings();
try { process = new ProcessBuilder(cmdline).directory(outputFolder).start();
process = new ProcessBuilder(cmdline).directory(outputFolder).start();
} catch (IOException e) {
throw new NoFFmpegException(e);
}
File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "export.log"); File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "export.log");
OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream(exportLogFile), ffmpegLog); OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream(exportLogFile), ffmpegLog);
new StreamPipe(process.getInputStream(), exportLogOut).start(); new StreamPipe(process.getInputStream(), exportLogOut).start();
@@ -159,16 +152,6 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
if (aborted) { if (aborted) {
return; return;
} }
try {
// Check whether this is a failure right at the beginning of the rendering process
// or at some later point (ffmpeg won't print the output file until the first frame
// has been written to stdin, so we can't already check for invalid args in <init>).
getVideoFile();
} catch (FFmpegStartupException e) {
// Possibly invalid ffmpeg arguments
renderer.setFailure(e);
return;
}
CrashReport report = CrashReport.makeCrashReport(t, "Exporting frame"); CrashReport report = CrashReport.makeCrashReport(t, "Exporting frame");
CrashReportCategory exportDetails = report.makeCategory("Export details"); CrashReportCategory exportDetails = report.makeCategory("Export details");
exportDetails.addCrashSection("Export command", settings.getExportCommand()); exportDetails.addCrashSection("Export command", settings.getExportCommand());
@@ -190,7 +173,7 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
aborted = true; aborted = true;
} }
public File getVideoFile() throws FFmpegStartupException { public File getVideoFile() {
String log = ffmpegLog.toString(); String log = ffmpegLog.toString();
for (String line : log.split("\n")) { for (String line : log.split("\n")) {
if (line.startsWith("Output #0")) { if (line.startsWith("Output #0")) {
@@ -198,30 +181,6 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
return new File(settings.getOutputFile().getParentFile(), fileName); return new File(settings.getOutputFile().getParentFile(), fileName);
} }
} }
throw new FFmpegStartupException(settings, log); throw new IllegalStateException("No output file found.");
}
public static class NoFFmpegException extends IOException {
public NoFFmpegException(Throwable cause) {
super(cause);
}
}
public static class FFmpegStartupException extends IOException {
private final RenderSettings settings;
private final String log;
public FFmpegStartupException(RenderSettings settings, String log) {
this.settings = settings;
this.log = log;
}
public RenderSettings getSettings() {
return settings;
}
public String getLog() {
return log;
}
} }
} }

View File

@@ -1,109 +0,0 @@
package com.replaymod.render.gui;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.VideoWriter;
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.GuiElement;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.ReportedException;
import java.util.Arrays;
import java.util.function.Consumer;
import static com.replaymod.render.ReplayModRender.LOGGER;
public class GuiExportFailed extends GuiScreen {
public static GuiExportFailed tryToRecover(VideoWriter.FFmpegStartupException e, Consumer<RenderSettings> doRestart) {
// Always log the error first
LOGGER.error("Rendering video:", e);
RenderSettings settings = e.getSettings();
// Check whether the user has configured some custom ffmpeg arguments
if (settings.getEncodingPreset().getValue().equals(settings.getExportArguments())) {
// If they haven't, then this is probably a faulty ffmpeg installation and there's nothing we can do
CrashReport crashReport = CrashReport.makeCrashReport(e, "Exporting video");
CrashReportCategory details = crashReport.makeCategory("Export details");
details.addCrashSection("Settings", settings);
details.addCrashSection("FFmpeg log", e.getLog());
throw new ReportedException(crashReport);
} else {
// If they have, ask them whether it was intentional
GuiExportFailed gui = new GuiExportFailed(e, doRestart);
gui.display();
return gui;
}
}
private final GuiLabel logLabel = new GuiLabel(this)
.setI18nText("replaymod.gui.rendering.error.ffmpeglog");
private final GuiVerticalList logList = new GuiVerticalList(this).setDrawShadow(true);
private final GuiButton resetButton = new GuiButton().setI18nLabel("gui.yes").setSize(100, 20);
private final GuiButton abortButton = new GuiButton().setI18nLabel("gui.no").setSize(100, 20);
private final GuiPanel info = new GuiPanel(this)
.setLayout(new VerticalLayout().setSpacing(4))
.addElements(new VerticalLayout.Data(0.5),
new GuiLabel().setI18nText("replaymod.gui.rendering.error.ffmpegargs.1"),
new GuiLabel().setI18nText("replaymod.gui.rendering.error.ffmpegargs.2"),
new GuiLabel(),
new GuiPanel().setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER).setSpacing(5))
.addElements(null, resetButton, abortButton)
);
{
setLayout(new CustomLayout<GuiScreen>() {
@Override
protected void layout(GuiScreen container, int width, int height) {
pos(info, width/2 - width(info)/2, (height/2 - height(info) - 30) / 2 + 30);
pos(logLabel, width/2 - width(logLabel)/2, height/2 + 4);
pos(logList, 10, y(logLabel) + height(logLabel) + 4);
size(logList, width - 10 - x(logList), height - 10 - y(logList));
}
});
setTitle(new GuiLabel().setI18nText("replaymod.gui.rendering.error.title"));
setBackground(Background.DIRT);
}
public GuiExportFailed(VideoWriter.FFmpegStartupException e, Consumer<RenderSettings> doRestart) {
logList.getListPanel().addElements(null,
Arrays.stream(e.getLog().replace("\t", " ").split("\n"))
.map(l -> new GuiLabel().setText(l))
.toArray(GuiElement[]::new));
resetButton.onClick(() -> {
RenderSettings oldSettings = e.getSettings();
doRestart.accept(new RenderSettings(
oldSettings.getRenderMethod(),
oldSettings.getEncodingPreset(),
oldSettings.getVideoWidth(),
oldSettings.getVideoHeight(),
oldSettings.getFramesPerSecond(),
oldSettings.getBitRate(),
oldSettings.getOutputFile(),
oldSettings.isRenderNameTags(),
oldSettings.isStabilizeYaw(),
oldSettings.isStabilizePitch(),
oldSettings.isStabilizeRoll(),
oldSettings.getChromaKeyingColor(),
oldSettings.isInject360Metadata(),
oldSettings.getAntiAliasing(),
oldSettings.getExportCommand(),
oldSettings.getEncodingPreset().getValue(),
oldSettings.isHighPerformance()
));
});
abortButton.onClick(() -> {
// Assume they know what they're doing
getMinecraft().displayGuiScreen(null);
});
}
}

View File

@@ -1,11 +1,8 @@
package com.replaymod.render.gui; package com.replaymod.render.gui;
import com.google.common.collect.Iterables;
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.replaymod.core.utils.Utils;
import com.replaymod.render.ReplayModRender; import com.replaymod.render.ReplayModRender;
import com.replaymod.render.VideoWriter;
import com.replaymod.render.rendering.VideoRenderer; import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.render.utils.RenderJob; import com.replaymod.render.utils.RenderJob;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
@@ -14,7 +11,6 @@ import com.replaymod.replaystudio.util.I18n;
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.AbstractGuiClickableContainer; import de.johni0702.minecraft.gui.container.AbstractGuiClickableContainer;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
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.GuiVerticalList; import de.johni0702.minecraft.gui.container.GuiVerticalList;
@@ -29,10 +25,12 @@ import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.gui.GuiErrorScreen; import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List; import java.util.List;
import static com.replaymod.render.ReplayModRender.LOGGER; import static com.replaymod.render.ReplayModRender.LOGGER;
@@ -64,8 +62,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
renameButton, removeButton), renameButton, removeButton),
closeButton); closeButton);
private final AbstractGuiScreen container; private final GuiContainer container;
private final ReplayHandler replayHandler;
private Entry selectedEntry; private Entry selectedEntry;
{ {
@@ -87,10 +84,9 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
}).addElements(null, title, list, buttonPanel); }).addElements(null, title, list, buttonPanel);
} }
public GuiRenderQueue(AbstractGuiScreen container, GuiRenderSettings guiRenderSettings, ReplayHandler replayHandler, Timeline timeline) { public GuiRenderQueue(GuiContainer container, GuiRenderSettings guiRenderSettings, ReplayHandler replayHandler, Timeline timeline) {
super(container); super(container);
this.container = container; this.container = container;
this.replayHandler = replayHandler;
LOGGER.trace("Opening render queue popup"); LOGGER.trace("Opening render queue popup");
setBackgroundColor(Colors.DARK_TRANSPARENT); setBackgroundColor(Colors.DARK_TRANSPARENT);
@@ -192,46 +188,30 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
renderButton.onClick(() -> { renderButton.onClick(() -> {
LOGGER.trace("Render button clicked"); LOGGER.trace("Render button clicked");
processQueue(queue); // Close all GUIs (so settings in GuiRenderSettings are saved)
getMinecraft().displayGuiScreen(null);
// Start rendering
for (RenderJob renderJob : queue) {
LOGGER.info("Starting render job {}", renderJob);
try {
VideoRenderer videoRenderer = new VideoRenderer(renderJob.getSettings(), replayHandler, renderJob.getTimeline());
videoRenderer.renderVideo();
} catch (IOException e) {
LOGGER.error("Rendering video:", e);
GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"),
I18n.format("replaymod.gui.rendering.error.message"));
getMinecraft().displayGuiScreen(errorScreen);
return;
} catch (Throwable t) {
CrashReport crashReport = CrashReport.makeCrashReport(t, "Rendering video");
throw new ReportedException(crashReport);
}
}
}); });
updateButtons(); updateButtons();
} }
private void processQueue(Iterable<RenderJob> queue) {
// Close all GUIs (so settings in GuiRenderSettings are saved)
getMinecraft().displayGuiScreen(null);
// Start rendering
int jobsDone = 0;
for (RenderJob renderJob : queue) {
LOGGER.info("Starting render job {}", renderJob);
try {
VideoRenderer videoRenderer = new VideoRenderer(renderJob.getSettings(), replayHandler, renderJob.getTimeline());
videoRenderer.renderVideo();
} catch (VideoWriter.NoFFmpegException e) {
LOGGER.error("Rendering video:", e);
GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"),
I18n.format("replaymod.gui.rendering.error.message"));
getMinecraft().displayGuiScreen(errorScreen);
return;
} catch (VideoWriter.FFmpegStartupException e) {
int jobsToSkip = jobsDone;
GuiExportFailed.tryToRecover(e, newSettings -> {
// Update current job with fixed ffmpeg arguments
renderJob.setSettings(newSettings);
// Restart queue, skipping the already completed jobs
processQueue(Iterables.skip(queue, jobsToSkip));
});
return;
} catch (Throwable t) {
Utils.error(LOGGER, this, CrashReport.makeCrashReport(t, "Rendering video"), () -> {});
container.display(); // Re-show the queue popup and the new error popup
return;
}
jobsDone++;
}
}
@Override @Override
public void open() { public void open() {
super.open(); super.open();

View File

@@ -7,7 +7,6 @@ import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator; import com.google.gson.InstanceCreator;
import com.replaymod.render.RenderSettings; import com.replaymod.render.RenderSettings;
import com.replaymod.render.ReplayModRender; import com.replaymod.render.ReplayModRender;
import com.replaymod.render.VideoWriter;
import com.replaymod.render.rendering.VideoRenderer; import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.replaystudio.pathing.path.Timeline;
@@ -30,6 +29,7 @@ import de.johni0702.minecraft.gui.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.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property; import net.minecraftforge.common.config.Property;
import org.lwjgl.util.Color; import org.lwjgl.util.Color;
@@ -39,13 +39,11 @@ import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import static com.replaymod.core.utils.Utils.error;
import static com.replaymod.render.ReplayModRender.LOGGER;
public class GuiRenderSettings extends GuiScreen implements Closeable { public class GuiRenderSettings extends GuiScreen implements Closeable {
public final GuiPanel contentPanel = new GuiPanel(this).setBackgroundColor(Colors.DARK_TRANSPARENT); public final GuiPanel contentPanel = new GuiPanel(this).setBackgroundColor(Colors.DARK_TRANSPARENT);
public final GuiVerticalList settingsList = new GuiVerticalList(contentPanel).setDrawSlider(true); public final GuiVerticalList settingsList = new GuiVerticalList(contentPanel).setDrawSlider(true);
@@ -201,21 +199,15 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
try { try {
VideoRenderer videoRenderer = new VideoRenderer(save(false), replayHandler, timeline); VideoRenderer videoRenderer = new VideoRenderer(save(false), replayHandler, timeline);
videoRenderer.renderVideo(); videoRenderer.renderVideo();
} catch (VideoWriter.NoFFmpegException e) { } catch (IOException e) {
LOGGER.error("Rendering video:", e); e.printStackTrace();
GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"), GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"),
I18n.format("replaymod.gui.rendering.error.message")); I18n.format("replaymod.gui.rendering.error.message"));
getMinecraft().displayGuiScreen(errorScreen); getMinecraft().displayGuiScreen(errorScreen);
} catch (VideoWriter.FFmpegStartupException e) {
GuiExportFailed.tryToRecover(e, newSettings -> {
// Update settings with fixed ffmpeg arguments
exportArguments.setText(newSettings.getExportArguments());
// Restart rendering, this will also save the changed ffmpeg arguments
renderButton.onClick();
});
} catch (Throwable t) { } catch (Throwable t) {
error(LOGGER, GuiRenderSettings.this, CrashReport.makeCrashReport(t, "Rendering video"), () -> {}); CrashReport crashReport = CrashReport.makeCrashReport(t, "Rendering video");
display(); // Re-show the render settings gui and the new error popup throw new ReportedException(crashReport);
} }
} }
}).setSize(100, 20).setI18nLabel("replaymod.gui.render"); }).setSize(100, 20).setI18nLabel("replaymod.gui.render");

View File

@@ -4,6 +4,7 @@ import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.*; import com.coremedia.iso.boxes.*;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import com.googlecode.mp4parser.BasicContainer; import com.googlecode.mp4parser.BasicContainer;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@@ -12,8 +13,6 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static com.replaymod.render.ReplayModRender.LOGGER;
public class MetadataInjector { public class MetadataInjector {
private static final String STITCHING_SOFTWARE = "Minecraft ReplayMod"; private static final String STITCHING_SOFTWARE = "Minecraft ReplayMod";
@@ -97,7 +96,7 @@ public class MetadataInjector {
videoFileOutputStream = new FileOutputStream(videoFile); videoFileOutputStream = new FileOutputStream(videoFile);
tempIsoFile.getBox(videoFileOutputStream.getChannel()); tempIsoFile.getBox(videoFileOutputStream.getChannel());
} catch(Exception e) { } catch(Exception e) {
LOGGER.error("360 Degree Metadata couldn't be injected", e); FMLLog.getLogger().error("360 Degree Metadata couldn't be injected", e);
} finally { } finally {
IOUtils.closeQuietly(tempIsoFile); IOUtils.closeQuietly(tempIsoFile);
IOUtils.closeQuietly(videoFileOutputStream); IOUtils.closeQuietly(videoFileOutputStream);

View File

@@ -1,23 +0,0 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.culling.Frustum;
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(Frustum.class)
public abstract class MixinFrustum {
@Inject(method = "isBoxInFrustum", at = @At("HEAD"), cancellable = true)
public void isBoxInFrustum(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CallbackInfoReturnable<Boolean> ci) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && handler.omnidirectional && handler.data == null) {
// Normally the camera is always facing the direction of the omnidirectional image face that is currently
// getting rendered. With ODS however, the camera is always facing forwards and the turning happens in the
// vertex shader (non-trivial due to stereo). As such, all chunks need to be rendered all the time for ODS.
ci.setReturnValue(true);
}
}
}

View File

@@ -39,7 +39,6 @@ import java.util.concurrent.Future;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import static com.google.common.collect.Iterables.getLast; import static com.google.common.collect.Iterables.getLast;
import static com.replaymod.render.ReplayModRender.LOGGER;
import static net.minecraft.client.renderer.GlStateManager.*; import static net.minecraft.client.renderer.GlStateManager.*;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
@@ -67,7 +66,6 @@ public class VideoRenderer implements RenderInfo {
private final GuiVideoRenderer gui; private final GuiVideoRenderer gui;
private boolean paused; private boolean paused;
private boolean cancelled; private boolean cancelled;
private volatile Throwable failureCause;
private Framebuffer guiFramebuffer; private Framebuffer guiFramebuffer;
private int displayWidth, displayHeight; private int displayWidth, displayHeight;
@@ -78,7 +76,7 @@ public class VideoRenderer implements RenderInfo {
this.timeline = timeline; this.timeline = timeline;
this.gui = new GuiVideoRenderer(this); this.gui = new GuiVideoRenderer(this);
this.renderingPipeline = Pipelines.newPipeline(settings.getRenderMethod(), this, this.renderingPipeline = Pipelines.newPipeline(settings.getRenderMethod(), this,
videoWriter = new VideoWriter(this) { videoWriter = new VideoWriter(settings) {
@Override @Override
public void consume(RGBFrame frame) { public void consume(RGBFrame frame) {
gui.updatePreview(frame); gui.updatePreview(frame);
@@ -91,7 +89,7 @@ public class VideoRenderer implements RenderInfo {
* Render this video. * Render this video.
* @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() throws Throwable { public boolean renderVideo() {
MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Pre(this)); MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Pre(this));
setup(); setup();
@@ -140,10 +138,6 @@ public class VideoRenderer implements RenderInfo {
MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Post(this)); MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Post(this));
if (failureCause != null) {
throw failureCause;
}
return !cancelled; return !cancelled;
} }
@@ -253,13 +247,7 @@ public class VideoRenderer implements RenderInfo {
new SoundHandler().playRenderSuccessSound(); new SoundHandler().playRenderSuccessSound();
try { new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();
if (!hasFailed()) {
new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();
}
} catch (VideoWriter.FFmpegStartupException e) {
setFailure(e);
}
// Finally, resize the Minecraft framebuffer to the actual width/height of the window // Finally, resize the Minecraft framebuffer to the actual width/height of the window
mc.resize(displayWidth, displayHeight); mc.resize(displayWidth, displayHeight);
@@ -335,7 +323,7 @@ public class VideoRenderer implements RenderInfo {
return; return;
} }
} }
} while (paused && !hasFailed()); } while (paused);
} }
private boolean displaySizeChanged() { private boolean displaySizeChanged() {
@@ -376,20 +364,6 @@ public class VideoRenderer implements RenderInfo {
renderingPipeline.cancel(); renderingPipeline.cancel();
} }
public boolean hasFailed() {
return failureCause != null;
}
public synchronized void setFailure(Throwable cause) {
if (this.failureCause != null) {
LOGGER.error("Further failure during failed rendering: ", cause);
} else {
LOGGER.error("Failure during rendering: ", cause);
this.failureCause = cause;
cancel();
}
}
private class TimelinePlayer extends AbstractTimelinePlayer { private class TimelinePlayer extends AbstractTimelinePlayer {
public TimelinePlayer(ReplayHandler replayHandler) { public TimelinePlayer(ReplayHandler replayHandler) {
super(replayHandler); super(replayHandler);

View File

@@ -12,7 +12,7 @@ import java.io.InputStream;
public class SoundHandler { public class SoundHandler {
private final ResourceLocation successSoundLocation = new ResourceLocation("replaymod", "render_success.wav"); private final ResourceLocation successSoundLocation = new ResourceLocation("replaymod", "renderSuccess.wav");
public void playRenderSuccessSound() { public void playRenderSuccessSound() {
playSound(successSoundLocation); playSound(successSoundLocation);

View File

@@ -147,7 +147,7 @@ public class InputReplayTimer extends WrappedTimer {
// 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) {
mc.ingameGUI.getChatGUI().clearChatMessages(); mc.ingameGUI.getChatGUI().clearChatMessages(false);
} }
if (key == 31 && Keyboard.isKeyDown(61)) { if (key == 31 && Keyboard.isKeyDown(61)) {

View File

@@ -124,13 +124,11 @@ public class ReplayHandler {
ReplayModReplay.instance.replayHandler = null; ReplayModReplay.instance.replayHandler = null;
mc.displayGuiScreen(null);
MinecraftForge.EVENT_BUS.post(new ReplayCloseEvent.Post(this)); MinecraftForge.EVENT_BUS.post(new ReplayCloseEvent.Post(this));
} }
private void setup() { private void setup() {
mc.ingameGUI.getChatGUI().clearChatMessages(); mc.ingameGUI.getChatGUI().clearChatMessages(true);
NetworkManager networkManager = new NetworkManager(EnumPacketDirection.CLIENTBOUND) { NetworkManager networkManager = new NetworkManager(EnumPacketDirection.CLIENTBOUND) {
@Override @Override

View File

@@ -35,7 +35,6 @@ import java.util.Optional;
@Mod(modid = ReplayModReplay.MOD_ID, @Mod(modid = ReplayModReplay.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModReplay { public class ReplayModReplay {
public static final String MOD_ID = "replaymod-replay"; public static final String MOD_ID = "replaymod-replay";
@@ -47,7 +46,7 @@ public class ReplayModReplay {
private final CameraControllerRegistry cameraControllerRegistry = new CameraControllerRegistry(); private final CameraControllerRegistry cameraControllerRegistry = new CameraControllerRegistry();
public static Logger LOGGER; private Logger logger;
protected ReplayHandler replayHandler; protected ReplayHandler replayHandler;
@@ -57,7 +56,7 @@ public class ReplayModReplay {
@Mod.EventHandler @Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) { public void preInit(FMLPreInitializationEvent event) {
LOGGER = event.getModLog(); logger = event.getModLog();
core = ReplayMod.instance; core = ReplayMod.instance;
core.getSettingsRegistry().register(Setting.class); core.getSettingsRegistry().register(Setting.class);
@@ -181,9 +180,6 @@ public class ReplayModReplay {
} }
public void startReplay(ReplayFile replayFile, boolean checkModCompat) throws IOException { public void startReplay(ReplayFile replayFile, boolean checkModCompat) throws IOException {
if (replayHandler != null) {
replayHandler.endReplay();
}
if (checkModCompat) { if (checkModCompat) {
ModCompat.ModInfoDifference modDifference = new ModCompat.ModInfoDifference(replayFile.getModInfo()); ModCompat.ModInfoDifference modDifference = new ModCompat.ModInfoDifference(replayFile.getModInfo());
if (!modDifference.getMissing().isEmpty() || !modDifference.getDiffering().isEmpty()) { if (!modDifference.getMissing().isEmpty() || !modDifference.getDiffering().isEmpty()) {
@@ -199,7 +195,7 @@ public class ReplayModReplay {
} }
public Logger getLogger() { public Logger getLogger() {
return LOGGER; return logger;
} }
public CameraControllerRegistry getCameraControllerRegistry() { public CameraControllerRegistry getCameraControllerRegistry() {

View File

@@ -2,16 +2,15 @@ package com.replaymod.replay;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.replaymod.core.ReplayMod; import com.google.common.util.concurrent.ListenableFutureTask;
import com.replaymod.core.utils.Restrictions; import com.replaymod.core.utils.Restrictions;
import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; 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.entity.EntityOtherPlayerMP;
import net.minecraft.client.gui.GuiDownloadTerrain; import net.minecraft.client.gui.GuiDownloadTerrain;
@@ -36,6 +35,7 @@ import java.io.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
/** /**
* Sends replay packets to netty channels. * Sends replay packets to netty channels.
@@ -43,7 +43,7 @@ import java.util.Map;
* the replay restart from the beginning. * the replay restart from the beginning.
*/ */
@Sharable @Sharable
public class ReplaySender extends ChannelDuplexHandler { public class ReplaySender extends ChannelInboundHandlerAdapter {
/** /**
* These packets are ignored completely during replay. * These packets are ignored completely during replay.
*/ */
@@ -242,7 +242,6 @@ public class ReplaySender extends ChannelDuplexHandler {
try { try {
channelInactive(ctx); channelInactive(ctx);
ctx.channel().pipeline().close(); ctx.channel().pipeline().close();
FileUtils.deleteDirectory(tempResourcePackFolder);
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -384,13 +383,6 @@ public class ReplaySender extends ChannelDuplexHandler {
if(BAD_PACKETS.contains(p.getClass())) return null; if(BAD_PACKETS.contains(p.getClass())) return null;
if (p instanceof SPacketCustomPayload) {
SPacketCustomPayload packet = (SPacketCustomPayload) p;
if ("MC|BOpen".equals(packet.getChannelName())) {
return null;
}
}
if(p instanceof SPacketResourcePackSend) { if(p instanceof SPacketResourcePackSend) {
SPacketResourcePackSend packet = (SPacketResourcePackSend) p; SPacketResourcePackSend packet = (SPacketResourcePackSend) p;
String url = packet.getURL(); String url = packet.getURL();
@@ -464,19 +456,22 @@ public class ReplaySender extends ChannelDuplexHandler {
} }
} }
new Runnable() { new Callable<Void>() {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void run() { public Void call() {
if (mc.theWorld == null || !mc.isCallingFromMinecraftThread()) { if (mc.theWorld == null || !mc.isCallingFromMinecraftThread()) {
ReplayMod.instance.runLater(this); synchronized(mc.scheduledTasks) {
return; mc.scheduledTasks.add(ListenableFutureTask.create(this));
}
return null;
} }
CameraEntity cent = replayHandler.getCameraEntity(); CameraEntity cent = replayHandler.getCameraEntity();
cent.setCameraPosition(ppl.getX(), ppl.getY(), ppl.getZ()); cent.setCameraPosition(ppl.getX(), ppl.getY(), ppl.getZ());
return null;
} }
}.run(); }.call();
} }
if(p instanceof SPacketChangeGameState) { if(p instanceof SPacketChangeGameState) {
@@ -513,22 +508,9 @@ public class ReplaySender extends ChannelDuplexHandler {
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// The embedded channel's event loop will consider every thread to be in it and as such provides no FileUtils.deleteDirectory(tempResourcePackFolder);
// guarantees that only one thread is using the pipeline at any one time. super.channelInactive(ctx);
// For reading the replay sender (either sync or async) is the only thread ever writing.
// For writing it may very well happen that multiple threads want to use the pipline at the same time.
// It's unclear whether the EmbeddedChannel is supposed to be thread-safe (the behavior of the event loop
// does suggest that). However it seems like it either isn't (likely) or there is a race condition.
// See: https://www.replaymod.com/forum/thread/1752#post8045 (https://paste.replaymod.com/lotacatuwo)
// To work around this issue, we just outright drop all write/flush requests (they aren't needed anyway).
// This still leaves channel handlers upstream with the threading issue but they all seem to cope well with it.
promise.setSuccess();
}
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
// See write method above
} }
/** /**
@@ -756,9 +738,6 @@ public class ReplaySender extends ChannelDuplexHandler {
} }
synchronized (this) { synchronized (this) {
if (timestamp == lastTimeStamp) { // Do nothing if we're already there
return;
}
if (timestamp < lastTimeStamp) { // Restart the replay if we need to go backwards in time if (timestamp < lastTimeStamp) { // Restart the replay if we need to go backwards in time
hasWorldLoaded = false; hasWorldLoaded = false;
lastTimeStamp = 0; lastTimeStamp = 0;

View File

@@ -5,7 +5,6 @@ import com.replaymod.core.events.SettingsChangedEvent;
import com.replaymod.core.utils.Utils; import com.replaymod.core.utils.Utils;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.Setting; import com.replaymod.replay.Setting;
import com.replaymod.replay.events.ReplayChatMessageEvent;
import com.replaymod.replaystudio.util.Location; import com.replaymod.replaystudio.util.Location;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -25,7 +24,6 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.text.ITextComponent;
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;
@@ -386,12 +384,6 @@ public class CameraEntity extends EntityPlayerSP {
&& (e instanceof EntityPlayer || e instanceof EntityLiving || e instanceof EntityItemFrame); && (e instanceof EntityPlayer || e instanceof EntityLiving || e instanceof EntityItemFrame);
} }
@Override
public void addChatMessage(ITextComponent message) {
if (MinecraftForge.EVENT_BUS.post(new ReplayChatMessageEvent(this))) return;
super.addChatMessage(message);
}
private class EventHandler { private class EventHandler {
@SubscribeEvent @SubscribeEvent
public void onPreClientTick(TickEvent.ClientTickEvent event) { public void onPreClientTick(TickEvent.ClientTickEvent event) {
@@ -468,45 +460,5 @@ public class CameraEntity extends EntityPlayerSP {
event.setRoll(roll); event.setRoll(roll);
} }
} }
private boolean heldItemTooltipsWasTrue;
@SubscribeEvent
public void preRenderGameOverlay(RenderGameOverlayEvent.Pre event) {
switch (event.getType()) {
case ALL:
heldItemTooltipsWasTrue = mc.gameSettings.heldItemTooltips;
mc.gameSettings.heldItemTooltips = false;
break;
case ARMOR:
case HEALTH:
case FOOD:
case AIR:
case HOTBAR:
case EXPERIENCE:
case HEALTHMOUNT:
case JUMPBAR:
case POTION_ICONS:
event.setCanceled(true);
break;
case HELMET:
case PORTAL:
case CROSSHAIRS:
case BOSSHEALTH:
case BOSSINFO:
case SUBTITLES:
case TEXT:
case CHAT:
case PLAYER_LIST:
case DEBUG:
break;
}
}
@SubscribeEvent
public void postRenderGameOverlay(RenderGameOverlayEvent.Post event) {
if (event.getType() != RenderGameOverlayEvent.ElementType.ALL) return;
mc.gameSettings.heldItemTooltips = heldItemTooltipsWasTrue;
}
} }
} }

View File

@@ -1,14 +0,0 @@
package com.replaymod.replay.events;
import com.replaymod.replay.camera.CameraEntity;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
@Cancelable
@RequiredArgsConstructor
public class ReplayChatMessageEvent extends Event {
@Getter
private final CameraEntity cameraEntity;
}

View File

@@ -28,7 +28,9 @@ import de.johni0702.minecraft.gui.utils.Consumer;
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;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOCase; import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.SuffixFileFilter; import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@@ -45,8 +47,6 @@ import java.io.IOException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import static com.replaymod.replay.ReplayModReplay.LOGGER;
public class GuiReplayViewer extends GuiScreen { public class GuiReplayViewer extends GuiScreen {
private final ReplayModReplay mod; private final ReplayModReplay mod;
@@ -91,7 +91,7 @@ public class GuiReplayViewer extends GuiScreen {
obj.consume(() -> new GuiReplayEntry(file, metaData, theThumb)); obj.consume(() -> new GuiReplayEntry(file, metaData, theThumb));
} }
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("Could not load Replay File {}", file.getName(), e); FMLLog.getLogger().error("Could not load Replay File " + file.getName(), e);
} }
} }
} catch (IOException e) { } catch (IOException e) {
@@ -155,7 +155,7 @@ public class GuiReplayViewer extends GuiScreen {
@Override @Override
public void run() { public void run() {
final File file = list.getSelected().file; final File file = list.getSelected().file;
String name = Utils.fileNameToReplayName(file.getName()); String name = FilenameUtils.getBaseName(file.getName());
final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(name); final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(name);
final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this, final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this,
new GuiLabel().setI18nText("replaymod.gui.viewer.rename.name").setColor(Colors.BLACK), new GuiLabel().setI18nText("replaymod.gui.viewer.rename.name").setColor(Colors.BLACK),
@@ -171,16 +171,16 @@ public class GuiReplayViewer extends GuiScreen {
} }
}).onTextChanged(obj -> { }).onTextChanged(obj -> {
popup.getYesButton().setEnabled(!nameField.getText().isEmpty() popup.getYesButton().setEnabled(!nameField.getText().isEmpty()
&& !new File(file.getParentFile(), Utils.replayNameToFileName(nameField.getText())).exists()); && !new File(file.getParentFile(), nameField.getText() + ".mcpr").exists());
}); });
Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() { Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
@Override @Override
public void onSuccess(Boolean delete) { public void onSuccess(Boolean delete) {
if (delete) { if (delete) {
// Sanitize their input // Sanitize their input
String name = nameField.getText().trim(); String name = nameField.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_");
// This file is what they want // This file is what they want
File targetFile = new File(file.getParentFile(), Utils.replayNameToFileName(name)); File targetFile = new File(file.getParentFile(), name + ".mcpr");
try { try {
// Finally, try to move it // Finally, try to move it
FileUtils.moveFile(file, targetFile); FileUtils.moveFile(file, targetFile);
@@ -302,7 +302,7 @@ public class GuiReplayViewer extends GuiScreen {
public GuiReplayEntry(File file, ReplayMetaData metaData, BufferedImage thumbImage) { public GuiReplayEntry(File file, ReplayMetaData metaData, BufferedImage thumbImage) {
this.file = file; this.file = file;
name.setText(ChatFormatting.UNDERLINE + Utils.fileNameToReplayName(file.getName())); name.setText(ChatFormatting.UNDERLINE + FilenameUtils.getBaseName(file.getName()));
if (Strings.isEmpty(metaData.getServerName())) { if (Strings.isEmpty(metaData.getServerName())) {
server.setI18nText("replaymod.gui.iphidden").setColor(Colors.DARK_RED); server.setI18nText("replaymod.gui.iphidden").setColor(Colors.DARK_RED);
} else { } else {

View File

@@ -0,0 +1,22 @@
package com.replaymod.replay.mixin;
import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.RenderArmorStand;
import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.entity.player.EntityPlayer;
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(RenderArmorStand.class)
public abstract class MixinRenderArmorStand {
@Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true)
private void replayModReplay_canRenderInvisibleName(EntityArmorStand entity, CallbackInfoReturnable<Boolean> ci) {
EntityPlayer thePlayer = Minecraft.getMinecraft().thePlayer;
if (thePlayer instanceof CameraEntity && entity.isInvisible()) {
ci.setReturnValue(false);
}
}
}

View File

@@ -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(int, int, int)} 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.
@@ -59,7 +59,7 @@ public abstract class MixinViewFrustum {
// 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.create(world, renderGlobal, 0)
).setOrigin(blockpos.getX(), blockpos.getY(), blockpos.getZ()); ).setPosition(blockpos.getX(), blockpos.getY(), blockpos.getZ());
} }
} }
} }

View File

@@ -19,7 +19,6 @@ import org.lwjgl.input.Keyboard;
@Mod(modid = ReplayModSimplePathing.MOD_ID, @Mod(modid = ReplayModSimplePathing.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModSimplePathing { public class ReplayModSimplePathing {
public static final String MOD_ID = "replaymod-simplepathing"; public static final String MOD_ID = "replaymod-simplepathing";

View File

@@ -1,11 +0,0 @@
{
"required": true,
"package": "com.replaymod.extras.playeroverview.mixin",
"mixins": [],
"server": [],
"client": [
"MixinRender"
],
"compatibilityLevel": "JAVA_8",
"refmap": "mixins.replaymod.refmap.json"
}

View File

@@ -3,7 +3,6 @@
"package": "com.replaymod.render.mixin", "package": "com.replaymod.render.mixin",
"mixins": [ "mixins": [
"MixinEntityRenderer", "MixinEntityRenderer",
"MixinFrustum",
"MixinParticleManager", "MixinParticleManager",
"MixinRender", "MixinRender",
"MixinRenderGlobal", "MixinRenderGlobal",

View File

@@ -5,6 +5,7 @@
"MixinGuiSpectator", "MixinGuiSpectator",
"MixinParticleManager", "MixinParticleManager",
"MixinPlayerControllerMP", "MixinPlayerControllerMP",
"MixinRenderArmorStand",
"MixinRenderArrow", "MixinRenderArrow",
"MixinRenderItem", "MixinRenderItem",
"MixinRenderLivingBase", "MixinRenderLivingBase",