Compare commits

..

1 Commits

Author SHA1 Message Date
Jonas Herzig
0b462280fc Update to 1.12.1 2017-08-04 09:10:48 +02:00
47 changed files with 148 additions and 682 deletions

View File

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

View File

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

View File

@@ -1,102 +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.world != null) {
// During replay, get ready to revert BetterSprinting's overwritten playerController
originalController = mc.playerController;
mc.world.addEventListener(worldAccessHook);
}
}
@SubscribeEvent(priority = EventPriority.LOW)
public void afterGuiOpenEvent(GuiOpenEvent event) {
if (ReplayModReplay.instance.getReplayHandler() != null && mc.world != null) {
mc.world.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 spawnParticle(int p_190570_1_, boolean p_190570_2_, boolean p_190570_3_, double p_190570_4_, double p_190570_6_, double p_190570_8_, double p_190570_10_, double p_190570_12_, double p_190570_14_, int... p_190570_16_) {}
@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.player 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.replay.replaymod.json");
Mixins.addConfiguration("mixins.compat.shaders.replaymod.json");
Mixins.addConfiguration("mixins.extras.playeroverview.replaymod.json");
CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
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.FMLPostInitializationEvent;
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 java.io.File;
@@ -39,7 +36,6 @@ import java.util.List;
useMetadata = true,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
guiFactory = "com.replaymod.core.gui.GuiFactory")
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) {
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) {
mc.scheduledTasks.add(ListenableFutureTask.create(() -> {
inRunLater = true;
try {
runnable.run();
} finally {
inRunLater = false;
}
}, null));
mc.scheduledTasks.add(ListenableFutureTask.create(runnable, null));
}
}

View File

@@ -1,6 +1,5 @@
package com.replaymod.core.utils;
import com.google.common.net.PercentEscaper;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
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.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import lombok.SneakyThrows;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.network.NetworkPlayerInfo;
import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.crash.CrashReport;
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.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension;
@@ -40,8 +36,6 @@ import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.KeyManagementException;
import java.security.KeyStore;
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,}))$");
}
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) {
NetworkPlayerInfo info = getMinecraft().getConnection().getPlayerInfo(uuid);
ResourceLocation skinLocation;

View File

@@ -11,7 +11,6 @@ import org.apache.logging.log4j.Logger;
@Mod(modid = ReplayModEditor.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true)
public class ReplayModEditor {
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 net.minecraft.crash.CrashReport;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
@@ -124,7 +125,7 @@ public class GuiReplayEditor extends GuiScreen {
}
public void save(File inputFile, PacketStream.FilterInfo...filters) {
save(Utils.fileNameToReplayName(inputFile.getName()), (outputFile) -> {
save(FilenameUtils.getBaseName(inputFile.getName()), (outputFile) -> {
Studio studio = new ReplayStudio();
File tmpDir = null;
try {
@@ -219,7 +220,7 @@ public class GuiReplayEditor extends GuiScreen {
Utils.error(LOGGER, GuiReplayEditor.this, CrashReport.makeCrashReport(e, "Getting replay folder"), null);
return;
}
File targetFile = new File(replayFolder, Utils.replayNameToFileName(name));
File targetFile = new File(replayFolder, name + ".mcpr");
if (targetFile.exists()) {
LOGGER.trace("Selected file already exists, asking for confirmation");
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 net.minecraft.client.resources.I18n;
import net.minecraft.crash.CrashReport;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.lwjgl.util.Dimension;
@@ -51,7 +52,7 @@ public class GuiTrimPanel extends GuiPanel {
public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this)
.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 startMin = newGuiNumberField();

View File

@@ -10,15 +10,11 @@ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Mod(modid = ReplayModExtras.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true)
public class ReplayModExtras {
public static final String MOD_ID = "replaymod-extras";
@@ -36,8 +32,6 @@ public class ReplayModExtras {
OpenEyeExtra.class
);
private final Map<Class<? extends Extra>, Extra> instances = new HashMap<>();
public static Logger LOGGER;
@Mod.EventHandler
@@ -51,14 +45,9 @@ public class ReplayModExtras {
try {
Extra extra = cls.newInstance();
extra.register(ReplayMod.instance);
instances.put(cls, extra);
} catch (Throwable 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.events.ReplayCloseEvent;
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.player.EntityPlayer;
import net.minecraftforge.client.event.RenderHandEvent;
@@ -56,6 +58,12 @@ public class PlayerOverview implements Extra {
});
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) {

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" +
"Type=Application\n" +
"Terminal=false\n" +
"NoDisplay=true\n" +
"MimeType=x-scheme-handler/replaymod;";
FileOutputStream out = new FileOutputStream(file);

View File

@@ -36,8 +36,6 @@ import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class YoutubeUploader {
private static final String CLIENT_ID = "743126594724-mfe7pj1k7e47uu5pk4503c8st9vj9ibu.apps.googleusercontent.com";
private static final String CLIENT_SECRET = "gMwcy3mRYCRamCIjJIYP7rqc";
@@ -139,7 +137,7 @@ public class YoutubeUploader {
CommandLine commandLine = new CommandLine(settings.getExportCommand());
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();
final AtomicBoolean active = new AtomicBoolean(true);
@@ -151,7 +149,7 @@ public class YoutubeUploader {
char c = (char) in.read();
if (c == '\r') {
String str = sb.toString();
LOGGER.debug("[FFmpeg] {}", str);
System.out.println(str);
if (str.startsWith("frame=")) {
str = str.substring(6).trim();
str = str.substring(0, str.indexOf(' '));

View File

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

View File

@@ -7,7 +7,8 @@ public enum MinecraftVersion {
MC_1_10_2("Minecraft 1.10.2", "1.10.2"),
MC_1_11("Minecraft 1.11", "1.11"),
MC_1_11_2("Minecraft 1.11.2", "1.11.2"),
MC_1_12("Minecraft 1.12", "1.12");
MC_1_12("Minecraft 1.12", "1.12"),
MC_1_12_1("Minecraft 1.12.1", "1.12.1");
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.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.util.Strings;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
@@ -49,8 +51,6 @@ import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import static com.replaymod.online.ReplayModOnline.LOGGER;
public class GuiReplayCenter extends GuiScreen {
private final ReplayModOnline mod;
private final ApiClient apiClient;
@@ -336,7 +336,7 @@ public class GuiReplayCenter extends GuiScreen {
}
});
} 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;
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",
"" + ChatFormatting.GRAY + ChatFormatting.ITALIC, fileInfo.getOwner());
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.Futures;
import com.replaymod.core.utils.Utils;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton;
@@ -28,7 +27,7 @@ public class GuiSaveModifiedReplay extends GuiScreen {
@Override
public void run() {
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()) {
Futures.addCallback(GuiYesNoPopup.open(GuiSaveModifiedReplay.this,
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.util.ReportedException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nullable;
@@ -134,7 +135,7 @@ public class GuiUploadReplay extends GuiScreen {
}
// Apply to gui
name.setText(Utils.fileNameToReplayName(file.getName()));
name.setText(FilenameUtils.getBaseName(file.getName()));
int secs = metaData.getDuration() / 1000;
durationLabel.setI18nText("replaymod.gui.upload.duration", secs / 60, secs % 60);
hideServerIP.setEnabled(!metaData.isSingleplayer());

View File

@@ -19,7 +19,6 @@ import org.lwjgl.input.Keyboard;
@Mod(modid = ReplayModRecording.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true)
public class ReplayModRecording {
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.utils.ModCompat;
import com.replaymod.core.utils.Utils;
import com.replaymod.recording.Setting;
import com.replaymod.recording.gui.GuiRecordingOverlay;
import com.replaymod.recording.packet.PacketListener;
@@ -78,7 +77,7 @@ public class ConnectionEventHandler {
File folder = core.getReplayFolder();
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.writeModInfo(ModCompat.getInstalledNetworkMods());

View File

@@ -15,12 +15,7 @@ import net.minecraft.network.EnumPacketDirection;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.network.play.server.SPacketCollectItem;
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.network.play.server.*;
import net.minecraft.util.text.TextComponentString;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
import org.apache.logging.log4j.LogManager;

View File

@@ -21,7 +21,6 @@ import java.util.List;
@Mod(modid = ReplayModRender.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true)
public class ReplayModRender {
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.rendering.FrameConsumer;
import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.StreamPipe;
import net.minecraft.client.Minecraft;
@@ -33,7 +32,6 @@ import static org.apache.commons.lang3.Validate.isTrue;
public class VideoWriter implements FrameConsumer<RGBFrame> {
private final VideoRenderer renderer;
private final RenderSettings settings;
private final Process process;
private final OutputStream outputStream;
@@ -43,9 +41,8 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
private ByteArrayOutputStream ffmpegLog = new ByteArrayOutputStream(4096);
public VideoWriter(final VideoRenderer renderer) throws IOException {
this.renderer = renderer;
this.settings = renderer.getRenderSettings();
public VideoWriter(final RenderSettings settings) throws IOException {
this.settings = settings;
File outputFolder = settings.getOutputFile().getParentFile();
FileUtils.forceMkdir(outputFolder);
@@ -60,13 +57,9 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
.replace("%FILTERS%", settings.getVideoFilters());
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();
try {
process = new ProcessBuilder(cmdline).directory(outputFolder).start();
} catch (IOException e) {
throw new NoFFmpegException(e);
}
process = new ProcessBuilder(cmdline).directory(outputFolder).start();
File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "export.log");
OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream(exportLogFile), ffmpegLog);
new StreamPipe(process.getInputStream(), exportLogOut).start();
@@ -159,16 +152,6 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
if (aborted) {
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");
CrashReportCategory exportDetails = report.makeCategory("Export details");
exportDetails.addCrashSection("Export command", settings.getExportCommand());
@@ -190,7 +173,7 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
aborted = true;
}
public File getVideoFile() throws FFmpegStartupException {
public File getVideoFile() {
String log = ffmpegLog.toString();
for (String line : log.split("\n")) {
if (line.startsWith("Output #0")) {
@@ -198,30 +181,6 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
return new File(settings.getOutputFile().getParentFile(), fileName);
}
}
throw new FFmpegStartupException(settings, log);
}
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;
}
throw new IllegalStateException("No output file found.");
}
}

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;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.replaymod.core.utils.Utils;
import com.replaymod.render.ReplayModRender;
import com.replaymod.render.VideoWriter;
import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.render.utils.RenderJob;
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.RenderInfo;
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.GuiPanel;
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 net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List;
import static com.replaymod.render.ReplayModRender.LOGGER;
@@ -64,8 +62,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
renameButton, removeButton),
closeButton);
private final AbstractGuiScreen container;
private final ReplayHandler replayHandler;
private final GuiContainer container;
private Entry selectedEntry;
{
@@ -87,10 +84,9 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
}).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);
this.container = container;
this.replayHandler = replayHandler;
LOGGER.trace("Opening render queue popup");
setBackgroundColor(Colors.DARK_TRANSPARENT);
@@ -192,46 +188,30 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
renderButton.onClick(() -> {
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();
}
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
public void open() {
super.open();

View File

@@ -7,7 +7,6 @@ import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.ReplayModRender;
import com.replaymod.render.VideoWriter;
import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.replay.ReplayHandler;
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.resources.I18n;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import org.lwjgl.util.Color;
@@ -39,13 +39,11 @@ import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
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 final GuiPanel contentPanel = new GuiPanel(this).setBackgroundColor(Colors.DARK_TRANSPARENT);
public final GuiVerticalList settingsList = new GuiVerticalList(contentPanel).setDrawSlider(true);
@@ -201,21 +199,15 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
try {
VideoRenderer videoRenderer = new VideoRenderer(save(false), replayHandler, timeline);
videoRenderer.renderVideo();
} catch (VideoWriter.NoFFmpegException e) {
LOGGER.error("Rendering video:", e);
} catch (IOException e) {
e.printStackTrace();
GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"),
I18n.format("replaymod.gui.rendering.error.message"));
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) {
error(LOGGER, GuiRenderSettings.this, CrashReport.makeCrashReport(t, "Rendering video"), () -> {});
display(); // Re-show the render settings gui and the new error popup
CrashReport crashReport = CrashReport.makeCrashReport(t, "Rendering video");
throw new ReportedException(crashReport);
}
}
}).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.google.common.primitives.Bytes;
import com.googlecode.mp4parser.BasicContainer;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
@@ -12,8 +13,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import static com.replaymod.render.ReplayModRender.LOGGER;
public class MetadataInjector {
private static final String STITCHING_SOFTWARE = "Minecraft ReplayMod";
@@ -97,7 +96,7 @@ public class MetadataInjector {
videoFileOutputStream = new FileOutputStream(videoFile);
tempIsoFile.getBox(videoFileOutputStream.getChannel());
} 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 {
IOUtils.closeQuietly(tempIsoFile);
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

@@ -40,7 +40,6 @@ import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import static com.google.common.collect.Iterables.getLast;
import static com.replaymod.render.ReplayModRender.LOGGER;
import static net.minecraft.client.renderer.GlStateManager.*;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
@@ -68,7 +67,6 @@ public class VideoRenderer implements RenderInfo {
private final GuiVideoRenderer gui;
private boolean paused;
private boolean cancelled;
private volatile Throwable failureCause;
private Framebuffer guiFramebuffer;
private int displayWidth, displayHeight;
@@ -79,7 +77,7 @@ public class VideoRenderer implements RenderInfo {
this.timeline = timeline;
this.gui = new GuiVideoRenderer(this);
this.renderingPipeline = Pipelines.newPipeline(settings.getRenderMethod(), this,
videoWriter = new VideoWriter(this) {
videoWriter = new VideoWriter(settings) {
@Override
public void consume(RGBFrame frame) {
gui.updatePreview(frame);
@@ -92,7 +90,7 @@ public class VideoRenderer implements RenderInfo {
* Render this video.
* @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));
setup();
@@ -141,10 +139,6 @@ public class VideoRenderer implements RenderInfo {
MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Post(this));
if (failureCause != null) {
throw failureCause;
}
return !cancelled;
}
@@ -254,13 +248,7 @@ public class VideoRenderer implements RenderInfo {
new SoundHandler().playRenderSuccessSound();
try {
if (!hasFailed()) {
new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();
}
} catch (VideoWriter.FFmpegStartupException e) {
setFailure(e);
}
new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();
// Finally, resize the Minecraft framebuffer to the actual width/height of the window
mc.resize(displayWidth, displayHeight);
@@ -336,7 +324,7 @@ public class VideoRenderer implements RenderInfo {
return;
}
}
} while (paused && !hasFailed());
} while (paused);
}
private boolean displaySizeChanged() {
@@ -377,20 +365,6 @@ public class VideoRenderer implements RenderInfo {
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 {
public TimelinePlayer(ReplayHandler replayHandler) {
super(replayHandler);

View File

@@ -12,7 +12,7 @@ import java.io.InputStream;
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() {
playSound(successSoundLocation);

View File

@@ -125,8 +125,6 @@ public class ReplayHandler {
ReplayModReplay.instance.replayHandler = null;
mc.displayGuiScreen(null);
MinecraftForge.EVENT_BUS.post(new ReplayCloseEvent.Post(this));
}

View File

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

View File

@@ -2,17 +2,16 @@ package com.replaymod.replay;
import com.google.common.base.Preconditions;
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.WrappedTimer;
import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replaystudio.replay.ReplayFile;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.ChannelInboundHandlerAdapter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityOtherPlayerMP;
import net.minecraft.client.gui.GuiDownloadTerrain;
@@ -37,6 +36,7 @@ import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* Sends replay packets to netty channels.
@@ -44,7 +44,7 @@ import java.util.Map;
* the replay restart from the beginning.
*/
@Sharable
public class ReplaySender extends ChannelDuplexHandler {
public class ReplaySender extends ChannelInboundHandlerAdapter {
/**
* These packets are ignored completely during replay.
*/
@@ -246,7 +246,6 @@ public class ReplaySender extends ChannelDuplexHandler {
try {
channelInactive(ctx);
ctx.channel().pipeline().close();
FileUtils.deleteDirectory(tempResourcePackFolder);
} catch(Exception e) {
e.printStackTrace();
}
@@ -388,13 +387,6 @@ public class ReplaySender extends ChannelDuplexHandler {
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) {
SPacketResourcePackSend packet = (SPacketResourcePackSend) p;
String url = packet.getURL();
@@ -468,19 +460,22 @@ public class ReplaySender extends ChannelDuplexHandler {
}
}
new Runnable() {
new Callable<Void>() {
@Override
@SuppressWarnings("unchecked")
public void run() {
public Void call() {
if (mc.world == null || !mc.isCallingFromMinecraftThread()) {
ReplayMod.instance.runLater(this);
return;
synchronized(mc.scheduledTasks) {
mc.scheduledTasks.add(ListenableFutureTask.create(this));
}
return null;
}
CameraEntity cent = replayHandler.getCameraEntity();
cent.setCameraPosition(ppl.getX(), ppl.getY(), ppl.getZ());
return null;
}
}.run();
}.call();
}
if(p instanceof SPacketChangeGameState) {
@@ -517,22 +512,9 @@ public class ReplaySender extends ChannelDuplexHandler {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
// The embedded channel's event loop will consider every thread to be in it and as such provides no
// guarantees that only one thread is using the pipeline at any one time.
// 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
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
FileUtils.deleteDirectory(tempResourcePackFolder);
super.channelInactive(ctx);
}
/**
@@ -760,9 +742,6 @@ public class ReplaySender extends ChannelDuplexHandler {
}
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
hasWorldLoaded = false;
lastTimeStamp = 0;

View File

@@ -5,7 +5,6 @@ import com.replaymod.core.events.SettingsChangedEvent;
import com.replaymod.core.utils.Utils;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.Setting;
import com.replaymod.replay.events.ReplayChatMessageEvent;
import com.replaymod.replaystudio.util.Location;
import lombok.Getter;
import lombok.Setter;
@@ -26,7 +25,6 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.World;
import net.minecraftforge.client.event.EntityViewRenderEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
@@ -387,12 +385,6 @@ public class CameraEntity extends EntityPlayerSP {
&& (e instanceof EntityPlayer || e instanceof EntityLiving || e instanceof EntityItemFrame);
}
@Override
public void sendMessage(ITextComponent message) {
if (MinecraftForge.EVENT_BUS.post(new ReplayChatMessageEvent(this))) return;
super.sendMessage(message);
}
private class EventHandler {
@SubscribeEvent
public void onPreClientTick(TickEvent.ClientTickEvent event) {
@@ -469,45 +461,5 @@ public class CameraEntity extends EntityPlayerSP {
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.resources.I18n;
import net.minecraft.util.Util;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.logging.log4j.LogManager;
@@ -45,8 +47,6 @@ import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import static com.replaymod.replay.ReplayModReplay.LOGGER;
public class GuiReplayViewer extends GuiScreen {
private final ReplayModReplay mod;
@@ -91,7 +91,7 @@ public class GuiReplayViewer extends GuiScreen {
obj.consume(() -> new GuiReplayEntry(file, metaData, theThumb));
}
} 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) {
@@ -155,7 +155,7 @@ public class GuiReplayViewer extends GuiScreen {
@Override
public void run() {
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 GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this,
new GuiLabel().setI18nText("replaymod.gui.viewer.rename.name").setColor(Colors.BLACK),
@@ -171,16 +171,16 @@ public class GuiReplayViewer extends GuiScreen {
}
}).onTextChanged(obj -> {
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>() {
@Override
public void onSuccess(Boolean delete) {
if (delete) {
// Sanitize their input
String name = nameField.getText().trim();
String name = nameField.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_");
// This file is what they want
File targetFile = new File(file.getParentFile(), Utils.replayNameToFileName(name));
File targetFile = new File(file.getParentFile(), name + ".mcpr");
try {
// Finally, try to move it
FileUtils.moveFile(file, targetFile);
@@ -302,7 +302,7 @@ public class GuiReplayViewer extends GuiScreen {
public GuiReplayEntry(File file, ReplayMetaData metaData, BufferedImage thumbImage) {
this.file = file;
name.setText(ChatFormatting.UNDERLINE + Utils.fileNameToReplayName(file.getName()));
name.setText(ChatFormatting.UNDERLINE + FilenameUtils.getBaseName(file.getName()));
if (Strings.isEmpty(metaData.getServerName())) {
server.setI18nText("replaymod.gui.iphidden").setColor(Colors.DARK_RED);
} 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().player;
if (thePlayer instanceof CameraEntity && entity.isInvisible()) {
ci.setReturnValue(false);
}
}
}

View File

@@ -19,7 +19,6 @@ import org.lwjgl.input.Keyboard;
@Mod(modid = ReplayModSimplePathing.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true)
public class ReplayModSimplePathing {
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",
"mixins": [
"MixinEntityRenderer",
"MixinFrustum",
"MixinParticleManager",
"MixinRender",
"MixinRenderGlobal",

View File

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