Compare commits

..

10 Commits

Author SHA1 Message Date
Jonas Herzig
0b462280fc Update to 1.12.1 2017-08-04 09:10:48 +02:00
Jonas Herzig
cae95d1c8f Fix usage of deprecated netty method 2017-06-29 10:48:47 +02:00
Jonas Herzig
67b7872c4d Update to first Forge 1.12 RB 2017-06-29 10:32:12 +02:00
Jonas Herzig
9b04d317c5 Suppress advancement and crafting unlocks (fixes #69) 2017-06-27 15:17:57 +02:00
Jonas Herzig
a6ffae2148 Update to Forge Build 2375 (fixes #70) 2017-06-27 15:07:54 +02:00
Jonas Herzig
6109960d4a Fix integration test failing because of conflicting keybindings 2017-06-15 20:44:10 +02:00
Jonas Herzig
4dcecc5697 Do not show OpenEye offer gui if there's no OpenEye for the current MC version 2017-06-15 16:28:44 +02:00
Jonas Herzig
bef5e41a8d Update mappings 2017-06-15 16:15:01 +02:00
Jonas Herzig
6ecb87e8dd Add workaround for race condition preventing us from joining SP (see MinecraftForge/MinecraftForge#3974)
This is done by preventing all calls to ChannelConfig.setAutoRead(false) during
the call to FMLNetworkHandler.fmlClientHandshake (the two @Redirects in
MixinNetworkDispatcher) and manually issuing the call before handleLoginSuccess
returns (at which point the NetHandlerPlayClient has been set).

This commit should be reverted once the issue mentioned above has been fixed.
2017-06-15 16:15:01 +02:00
Jonas Herzig
921c9e0098 Update to 1.12 2017-06-15 16:15:01 +02:00
73 changed files with 295 additions and 785 deletions

View File

@@ -17,7 +17,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT' classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT'
classpath 'org.spongepowered:mixingradle:0.4-SNAPSHOT' classpath 'org.spongepowered:mixingradle:0.4-SNAPSHOT'
} }
} }
@@ -34,13 +34,11 @@ archivesBaseName = "replaymod"
minecraft { minecraft {
coreMod = 'com.replaymod.core.LoadingPlugin' coreMod = 'com.replaymod.core.LoadingPlugin'
version = '1.11.2-13.20.0.2216' version = '1.12.1-14.22.0.2444'
runDir = "eclipse" runDir = "eclipse"
mappings = "snapshot_20161220" mappings = "snapshot_20170615"
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 {
@@ -48,9 +46,6 @@ repositories {
name = "SpongePowered Repo" name = "SpongePowered Repo"
url = "http://repo.spongepowered.org/maven/" url = "http://repo.spongepowered.org/maven/"
} }
maven {
url 'https://repo.spacehq.org/content/repositories/snapshots/'
}
} }
configurations { configurations {
@@ -61,7 +56,9 @@ configurations {
dependencies { dependencies {
compile 'org.projectlombok:lombok:1.16.4' compile 'org.projectlombok:lombok:1.16.4'
compile 'org.spongepowered:mixin:0.6.8-SNAPSHOT' compile('org.spongepowered:mixin:0.6.11-SNAPSHOT') {
exclude group: 'org.apache.logging.log4j'
}
shade 'com.googlecode.mp4parser:isoparser:1.1.7' shade 'com.googlecode.mp4parser:isoparser:1.1.7'
shade 'org.apache.commons:commons-exec:1.3' shade 'org.apache.commons:commons-exec:1.3'
shade 'com.google.apis:google-api-services-youtube:v3-rev178-1.22.0' shade 'com.google.apis:google-api-services-youtube:v3-rev178-1.22.0'

2
jGui

Submodule jGui updated: bd3fa93d7d...e5fcc65032

View File

@@ -299,7 +299,7 @@ public abstract class AbstractTask implements Task {
return; return;
} }
clickNow(button.xPosition + 5, button.yPosition + 5); clickNow(button.x + 5, button.y + 5);
} catch (IllegalAccessException | NoSuchFieldException e) { } catch (IllegalAccessException | NoSuchFieldException e) {
future.setException(e); future.setException(e);
} }

View File

@@ -36,6 +36,11 @@ public class ReplayModIntegrationTest {
// Make sure the game window doesn't have to remain in focus during the test // Make sure the game window doesn't have to remain in focus during the test
mc.gameSettings.pauseOnLostFocus = false; mc.gameSettings.pauseOnLostFocus = false;
// Vanilla uses our keys
mc.gameSettings.keyBindAdvancements.setKeyCode(0);
mc.gameSettings.keyBindLoadToolbar.setKeyCode(0);
mc.gameSettings.keyBindSaveToolbar.setKeyCode(0);
runTasks( runTasks(
new SkipLogin(), new SkipLogin(),
new DownloadOpenEye(), new DownloadOpenEye(),

View File

@@ -10,6 +10,10 @@ import java.nio.file.NoSuchFileException;
public class DownloadOpenEye extends AbstractTask { public class DownloadOpenEye extends AbstractTask {
@Override @Override
protected void init() { protected void init() {
if ("1".equals(System.getenv("RM_INTEGRATION_TEST_NO_OPENEYE"))) {
runLater(() -> future.set(null));
return;
}
expectGui(OpenEyeExtra.OfferGui.class, offerGui -> { expectGui(OpenEyeExtra.OfferGui.class, offerGui -> {
click(offerGui.yesButton); click(offerGui.yesButton);
expectGuiClosed(20 * 1000, () -> { expectGuiClosed(20 * 1000, () -> {

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,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

@@ -30,7 +30,7 @@ public class ShaderBeginRender {
// check if Shaders are enabled // check if Shaders are enabled
if (!(boolean) (ShaderReflection.config_isShaders.invoke(null))) return; if (!(boolean) (ShaderReflection.config_isShaders.invoke(null))) return;
ShaderReflection.shaders_beginRender.invoke(null, mc, mc.timer.elapsedPartialTicks, 0); ShaderReflection.shaders_beginRender.invoke(null, mc, mc.timer.renderPartialTicks, 0);
} catch (IllegalAccessException | InvocationTargetException e) { } catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@@ -20,6 +20,7 @@ public abstract class MixinShaderRenderChunk {
*/ */
@Inject(method = "isPlayerUpdate", at = @At("HEAD"), cancellable = true) @Inject(method = "isPlayerUpdate", at = @At("HEAD"), cancellable = true)
private void replayModCompat_disableIsPlayerUpdate(CallbackInfoReturnable<Boolean> ci) { private void replayModCompat_disableIsPlayerUpdate(CallbackInfoReturnable<Boolean> ci) {
// TODO: Update to 1.12 once optifine is available
if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return; if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return;
ci.setReturnValue(true); ci.setReturnValue(true);
} }

View File

@@ -85,7 +85,7 @@ public class KeyBindingRegistry {
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding"); CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding");
CrashReportCategory category = crashReport.makeCategory("Key Binding"); CrashReportCategory category = crashReport.makeCategory("Key Binding");
category.addCrashSection("Key Binding", keyBinding); category.addCrashSection("Key Binding", keyBinding);
category.setDetail("Handler", runnable::toString); category.addDetail("Handler", runnable::toString);
throw new ReportedException(crashReport); throw new ReportedException(crashReport);
} }
} }
@@ -100,7 +100,7 @@ public class KeyBindingRegistry {
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Raw Key Binding"); CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Raw Key Binding");
CrashReportCategory category = crashReport.makeCategory("Key Binding"); CrashReportCategory category = crashReport.makeCategory("Key Binding");
category.addCrashSection("Key Code", keyCode); category.addCrashSection("Key Code", keyCode);
category.setDetail("Handler", runnable::toString); category.addDetail("Handler", runnable::toString);
throw new ReportedException(crashReport); throw new ReportedException(crashReport);
} }
} }

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

@@ -15,30 +15,17 @@ public class GuiFactory implements IModGuiFactory {
} }
@Override @Override
public Class<? extends GuiScreen> mainConfigGuiClass() { public boolean hasConfigGui() {
return ConfigGuiWrapper.class; return true;
}
@Override
public GuiScreen createConfigGui(GuiScreen parentScreen) {
return new GuiReplaySettings(parentScreen, ReplayMod.instance.getSettingsRegistry()).toMinecraft();
} }
@Override @Override
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() { public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() {
return null; return null;
} }
@Override
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
return null;
}
public static class ConfigGuiWrapper extends GuiScreen {
private final GuiScreen parent;
public ConfigGuiWrapper(GuiScreen parent) {
this.parent = parent;
}
@Override
public void initGui() {
new GuiReplaySettings(parent, ReplayMod.instance.getSettingsRegistry()).display();
}
}
} }

View File

@@ -19,12 +19,12 @@ public class MainMenuHandler {
if (event.getGui() instanceof GuiMainMenu) { if (event.getGui() instanceof GuiMainMenu) {
for (GuiButton button : event.getButtonList()) { for (GuiButton button : event.getButtonList()) {
// Buttons that aren't in a rectangle directly above our space don't need moving // Buttons that aren't in a rectangle directly above our space don't need moving
if (button.xPosition + button.width < event.getGui().width / 2 - 100 if (button.x + button.width < event.getGui().width / 2 - 100
|| button.xPosition > event.getGui().width / 2 + 100 || button.x > event.getGui().width / 2 + 100
|| button.yPosition > event.getGui().height / 4 + 10 + 4 * 24) continue; || button.y > event.getGui().height / 4 + 10 + 4 * 24) continue;
// Move button up to make space for two rows of buttons // Move button up to make space for two rows of buttons
// and then move back down by 10 to compensate for the space to the exit button that was already there // and then move back down by 10 to compensate for the space to the exit button that was already there
button.yPosition -= 2 * 24 - 10; button.y -= 2 * 24 - 10;
} }
} }
} }

View File

@@ -4,22 +4,22 @@ import com.replaymod.replaystudio.data.ModInfo;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.registry.GameData; import net.minecraftforge.registries.ForgeRegistry;
import net.minecraftforge.registries.RegistryManager;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ModCompat { public class ModCompat {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Collection<ModInfo> getInstalledNetworkMods() { public static Collection<ModInfo> getInstalledNetworkMods() {
Map<String, ModContainer> ignoreCaseMap = Loader.instance().getModList().stream() Map<String, ModContainer> ignoreCaseMap = Loader.instance().getModList().stream()
.collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity())); .collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity()));
return Stream.concat( return RegistryManager.ACTIVE.takeSnapshot(false).keySet().stream()
((Set<ResourceLocation>) GameData.getBlockRegistry().getKeys()).stream(), .map(RegistryManager.ACTIVE::getRegistry)
((Set<ResourceLocation>) GameData.getItemRegistry().getKeys()).stream() .map(ForgeRegistry::getKeys).flatMap(Set::stream)
).map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct() .map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct()
.map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null) .map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null)
.map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion())) .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion()))
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

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

View File

@@ -1,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

@@ -3,6 +3,8 @@ package com.replaymod.core.utils;
import net.minecraft.util.Timer; import net.minecraft.util.Timer;
public class WrappedTimer extends Timer { public class WrappedTimer extends Timer {
public static final float DEFAULT_MS_PER_TICK = 1000 / 20;
protected final Timer wrapped; protected final Timer wrapped;
public WrappedTimer(Timer wrapped) { public WrappedTimer(Timer wrapped) {
@@ -19,15 +21,10 @@ public class WrappedTimer extends Timer {
} }
protected void copy(Timer from, Timer to) { protected void copy(Timer from, Timer to) {
to.ticksPerSecond = from.ticksPerSecond;
to.lastHRTime = from.lastHRTime;
to.elapsedTicks = from.elapsedTicks; to.elapsedTicks = from.elapsedTicks;
to.renderPartialTicks = from.renderPartialTicks; to.renderPartialTicks = from.renderPartialTicks;
to.timerSpeed = from.timerSpeed;
to.elapsedPartialTicks = from.elapsedPartialTicks;
to.lastSyncSysClock = from.lastSyncSysClock; to.lastSyncSysClock = from.lastSyncSysClock;
to.lastSyncHRClock = from.lastSyncHRClock; to.elapsedPartialTicks = from.elapsedPartialTicks;
to.counter = from.counter; to.tickLength = from.tickLength;
to.timeSyncAdjustment = from.timeSyncAdjustment;
} }
} }

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

@@ -26,6 +26,7 @@ import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY; import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class OpenEyeExtra implements Extra { public class OpenEyeExtra implements Extra {
private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + Loader.MC_VERSION; private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + Loader.MC_VERSION;
@@ -39,7 +40,25 @@ public class OpenEyeExtra implements Extra {
mod.getSettingsRegistry().register(ASK_FOR_OPEN_EYE); mod.getSettingsRegistry().register(ASK_FOR_OPEN_EYE);
if (!Loader.isModLoaded("OpenEye") && mod.getSettingsRegistry().get(ASK_FOR_OPEN_EYE)) { if (!Loader.isModLoaded("OpenEye") && mod.getSettingsRegistry().get(ASK_FOR_OPEN_EYE)) {
mod.runLater(() -> new OfferGui(GuiScreen.wrap(mod.getMinecraft().currentScreen)).display()); new Thread(() -> {
try {
LOGGER.trace("Checking for OpenEye availability");
HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection();
connection.setSSLSocketFactory(SSL_SOCKET_FACTORY);
connection.setRequestMethod("HEAD");
connection.connect();
LOGGER.trace("Got response code: {}", connection.getResponseCode());
if (connection.getResponseCode() == 200) {
mod.runLater(() -> new OfferGui(GuiScreen.wrap(mod.getMinecraft().currentScreen)).display());
} else {
LOGGER.info("Cannot offer OpenEye, server returned: {} {}",
connection.getResponseCode(), connection.getResponseMessage());
}
connection.disconnect();
} catch (Throwable e) {
LOGGER.error("Failed to check for OpenEye availability:", e);
}
}).start();
} }
} }

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

@@ -6,7 +6,9 @@ public enum MinecraftVersion {
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"), MC_1_11("Minecraft 1.11", "1.11"),
MC_1_11_2("Minecraft 1.11.2", "1.11.2"); MC_1_11_2("Minecraft 1.11.2", "1.11.2"),
MC_1_12("Minecraft 1.12", "1.12"),
MC_1_12_1("Minecraft 1.12.1", "1.12.1");
private String niceName, apiName; private String niceName, apiName;

View File

@@ -55,7 +55,7 @@ public class GuiRegister extends AbstractGuiScreen<GuiRegister> {
pos(cancelButton, width / 2 + 2, 170); pos(cancelButton, width / 2 + 2, 170);
pos(statusLabel, width / 2 - statusLabel.getMinSize().getWidth() / 2, 152); pos(statusLabel, width / 2 - statusLabel.getMinSize().getWidth() / 2, 152);
FontRenderer font = getMinecraft().fontRendererObj; FontRenderer font = getMinecraft().fontRenderer;
int lineCount = font.listFormattedStringToWidth(disclaimerLabel.getText(), width - 10).size(); int lineCount = font.listFormattedStringToWidth(disclaimerLabel.getText(), width - 10).size();
Dimension dim = new Dimension(width - 10, font.FONT_HEIGHT * lineCount); Dimension dim = new Dimension(width - 10, font.FONT_HEIGHT * lineCount);
disclaimerLabel.setSize(dim); disclaimerLabel.setSize(dim);

View File

@@ -37,7 +37,9 @@ 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 org.apache.logging.log4j.core.helpers.Strings; 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.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

@@ -6,6 +6,7 @@ import com.google.common.collect.Ordering;
import com.google.common.primitives.Longs; import com.google.common.primitives.Longs;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.core.utils.WrappedTimer;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Keyframe; import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Path;
@@ -68,8 +69,8 @@ public abstract class AbstractTimelinePlayer {
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
lastTime = 0; lastTime = 0;
mc.timer = new ReplayTimer(mc.timer); mc.timer = new ReplayTimer(mc.timer);
mc.timer.timerSpeed = 1; mc.timer.tickLength = WrappedTimer.DEFAULT_MS_PER_TICK;
mc.timer.elapsedPartialTicks = mc.timer.elapsedTicks = 0; mc.timer.renderPartialTicks = mc.timer.elapsedTicks = 0;
return future = settableFuture = SettableFuture.create(); return future = settableFuture = SettableFuture.create();
} }
@@ -107,10 +108,9 @@ public abstract class AbstractTimelinePlayer {
float timeInTicks = replayTime / 50f; float timeInTicks = replayTime / 50f;
float previousTimeInTicks = lastTime / 50f; float previousTimeInTicks = lastTime / 50f;
float passedTicks = timeInTicks - previousTimeInTicks; float passedTicks = timeInTicks - previousTimeInTicks;
mc.timer.elapsedPartialTicks += passedTicks; mc.timer.renderPartialTicks += passedTicks;
mc.timer.elapsedTicks = (int) mc.timer.elapsedPartialTicks; mc.timer.elapsedTicks = (int) mc.timer.renderPartialTicks;
mc.timer.elapsedPartialTicks -= mc.timer.elapsedTicks; mc.timer.renderPartialTicks -= mc.timer.elapsedTicks;
mc.timer.renderPartialTicks = mc.timer.elapsedPartialTicks;
lastTime = replayTime; lastTime = replayTime;

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

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

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;
@@ -47,7 +46,7 @@ public class ConnectionEventHandler {
try { try {
boolean local = networkManager.isLocalChannel(); boolean local = networkManager.isLocalChannel();
if (local) { if (local) {
if (mc.getIntegratedServer().getEntityWorld().getWorldType() == WorldType.DEBUG_WORLD) { if (mc.getIntegratedServer().getEntityWorld().getWorldType() == WorldType.DEBUG_ALL_BLOCK_STATES) {
logger.info("Debug World recording is not supported."); logger.info("Debug World recording is not supported.");
return; return;
} }
@@ -78,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

@@ -235,7 +235,7 @@ public class RecordingEventHandler {
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())); event.pickedUp.getItem().getMaxStackSize()));
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@@ -24,4 +24,9 @@ public abstract class MixinNetHandlerLoginClient {
public void replayModRecording_initiateRecording(CallbackInfo cb) { public void replayModRecording_initiateRecording(CallbackInfo cb) {
ReplayModRecording.instance.initiateRecording(networkManager); ReplayModRecording.instance.initiateRecording(networkManager);
} }
@Inject(method = "handleLoginSuccess", at=@At("RETURN"))
public void replayModRecording_raceConditionWorkAround(CallbackInfo cb) {
networkManager.channel().config().setAutoRead(true);
}
} }

View File

@@ -1,8 +1,11 @@
package com.replaymod.recording.mixin; package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.FMLHandshakeFilter; import com.replaymod.recording.handler.FMLHandshakeFilter;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.NetworkManager;
import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec; import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher; import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.Side;
@@ -10,6 +13,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = NetworkDispatcher.class, remap = false) @Mixin(value = NetworkDispatcher.class, remap = false)
@@ -43,4 +47,17 @@ public abstract class MixinNetworkDispatcher {
"replaymod_filter", new FMLHandshakeFilter()); "replaymod_filter", new FMLHandshakeFilter());
} }
} }
@Redirect(method = "clientListenForServerHandshake", at = @At(value = "INVOKE", remap = true, target =
"Lnet/minecraft/network/NetworkManager;setConnectionState(Lnet/minecraft/network/EnumConnectionState;)V"))
public void replayModRecording_raceConditionWorkAround1(NetworkManager self, EnumConnectionState ignored) { }
@Redirect(method = "insertIntoChannel", at = @At(value = "INVOKE", target =
"Lio/netty/channel/ChannelConfig;setAutoRead(Z)Lio/netty/channel/ChannelConfig;"))
public ChannelConfig replayModRecording_raceConditionWorkAround2(ChannelConfig self, boolean autoRead) {
if (side == Side.CLIENT) {
autoRead = false;
}
return self.setAutoRead(autoRead);
}
} }

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;
@@ -202,7 +197,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private byte[] getPacketData(Packet packet) throws IOException { private byte[] getPacketData(Packet packet) throws Exception {
if (packet instanceof SPacketSpawnMob) { if (packet instanceof SPacketSpawnMob) {
SPacketSpawnMob p = (SPacketSpawnMob) packet; SPacketSpawnMob p = (SPacketSpawnMob) packet;
if (p.dataManager == null) { if (p.dataManager == null) {

View File

@@ -88,7 +88,7 @@ public class ResourcePackRecorder {
if (levelDir.isFile()) { if (levelDir.isFile()) {
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.ACCEPTED)); netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.ACCEPTED));
Futures.addCallback(mc.getResourcePackRepository().setResourcePackInstance(levelDir), new FutureCallback<Object>() { Futures.addCallback(mc.getResourcePackRepository().setServerResourcePack(levelDir), new FutureCallback<Object>() {
@Override @Override
public void onSuccess(Object result) { public void onSuccess(Object result) {
recordResourcePack(levelDir, requestId); recordResourcePack(levelDir, requestId);
@@ -174,7 +174,7 @@ public class ResourcePackRecorder {
String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString(); String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
if (fileHash.equals(hash)) { if (fileHash.equals(hash)) {
recordResourcePack(file, requestId); recordResourcePack(file, requestId);
return repo.setResourcePackInstance(file); return repo.setServerResourcePack(file);
} }
logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it."); logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it.");
@@ -196,7 +196,7 @@ public class ResourcePackRecorder {
@Override @Override
public void onSuccess(Object value) { public void onSuccess(Object value) {
recordResourcePack(file, requestId); recordResourcePack(file, requestId);
repo.setResourcePackInstance(file); repo.setServerResourcePack(file);
} }
@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

@@ -4,7 +4,7 @@ import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleManager; import net.minecraft.client.particle.ParticleManager;
import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -12,19 +12,19 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ParticleManager.class) @Mixin(ParticleManager.class)
public abstract class MixinParticleManager { public abstract class MixinParticleManager {
@Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/Particle;renderParticle(Lnet/minecraft/client/renderer/VertexBuffer;Lnet/minecraft/entity/Entity;FFFFFF)V")) @Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/Particle;renderParticle(Lnet/minecraft/client/renderer/BufferBuilder;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderNormalParticle(Particle particle, VertexBuffer vertexBuffer, Entity view, float partialTicks, private void renderNormalParticle(Particle particle, BufferBuilder vertexBuffer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(particle, vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); renderParticle(particle, vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
} }
@Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/Particle;renderParticle(Lnet/minecraft/client/renderer/VertexBuffer;Lnet/minecraft/entity/Entity;FFFFFF)V")) @Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/Particle;renderParticle(Lnet/minecraft/client/renderer/BufferBuilder;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderLitParticle(Particle particle, VertexBuffer vertexBuffer, Entity view, float partialTicks, private void renderLitParticle(Particle particle, BufferBuilder vertexBuffer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(particle, vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); renderParticle(particle, vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
} }
private void renderParticle(Particle particle, VertexBuffer vertexBuffer, Entity view, float partialTicks, private void renderParticle(Particle particle, BufferBuilder vertexBuffer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler(); EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && handler.omnidirectional) { if (handler != null && handler.omnidirectional) {

View File

@@ -1,5 +1,6 @@
package com.replaymod.render.rendering; package com.replaymod.render.rendering;
import com.replaymod.core.utils.WrappedTimer;
import com.replaymod.pathing.player.AbstractTimelinePlayer; import com.replaymod.pathing.player.AbstractTimelinePlayer;
import com.replaymod.pathing.player.ReplayTimer; import com.replaymod.pathing.player.ReplayTimer;
import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.pathing.properties.TimestampProperty;
@@ -39,7 +40,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 +67,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 +77,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 +90,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();
@@ -113,8 +112,8 @@ public class VideoRenderer implements RenderInfo {
if (videoStart > 1000) { if (videoStart > 1000) {
int replayTime = videoStart - 1000; int replayTime = videoStart - 1000;
timer.elapsedPartialTicks = timer.renderPartialTicks = 0; timer.renderPartialTicks = 0;
timer.timerSpeed = 1; timer.tickLength = WrappedTimer.DEFAULT_MS_PER_TICK;
while (replayTime < videoStart) { while (replayTime < videoStart) {
timer.elapsedTicks = 1; timer.elapsedTicks = 1;
replayTime += 50; replayTime += 50;
@@ -140,10 +139,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 +248,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 +324,7 @@ public class VideoRenderer implements RenderInfo {
return; return;
} }
} }
} while (paused && !hasFailed()); } while (paused);
} }
private boolean displaySizeChanged() { private boolean displaySizeChanged() {
@@ -376,20 +365,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

@@ -3,6 +3,7 @@ package com.replaymod.replay;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.replaymod.core.utils.Restrictions; import com.replaymod.core.utils.Restrictions;
import com.replaymod.core.utils.WrappedTimer;
import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.camera.SpectatorCameraController; import com.replaymod.replay.camera.SpectatorCameraController;
import com.replaymod.replay.events.ReplayCloseEvent; import com.replaymod.replay.events.ReplayCloseEvent;
@@ -119,13 +120,11 @@ public class ReplayHandler {
mc.loadWorld(null); mc.loadWorld(null);
} }
mc.timer.timerSpeed = 1; mc.timer.tickLength = WrappedTimer.DEFAULT_MS_PER_TICK;
overlay.setVisible(false); overlay.setVisible(false);
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));
} }
@@ -143,13 +142,14 @@ public class ReplayHandler {
networkManager.setNetHandler(netHandlerPlayClient); networkManager.setNetHandler(netHandlerPlayClient);
FMLClientHandler.instance().setPlayClient(netHandlerPlayClient); FMLClientHandler.instance().setPlayClient(netHandlerPlayClient);
channel = new EmbeddedChannel(networkManager); channel = new EmbeddedChannel();
NetworkDispatcher networkDispatcher = new NetworkDispatcher(networkManager); NetworkDispatcher networkDispatcher = new NetworkDispatcher(networkManager);
channel.attr(NetworkDispatcher.FML_DISPATCHER).set(networkDispatcher); channel.attr(NetworkDispatcher.FML_DISPATCHER).set(networkDispatcher);
channel.pipeline().addFirst("ReplayModReplay_replaySender", replaySender); channel.pipeline().addFirst("ReplayModReplay_replaySender", replaySender);
channel.pipeline().addAfter("ReplayModReplay_replaySender", "fml:packet_handler", networkDispatcher); channel.pipeline().addLast("packet_handler", networkManager);
channel.pipeline().fireChannelActive(); channel.pipeline().fireChannelActive();
networkDispatcher.clientToServerHandshake();
} }
public ReplayFile getReplayFile() { public ReplayFile getReplayFile() {
@@ -301,7 +301,7 @@ public class ReplayHandler {
@Override @Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) { public void drawScreen(int mouseX, int mouseY, float partialTicks) {
drawBackground(0); drawBackground(0);
drawCenteredString(fontRendererObj, I18n.format("replaymod.gui.pleasewait"), drawCenteredString(fontRenderer, I18n.format("replaymod.gui.pleasewait"),
width / 2, height / 2, 0xffffffff); width / 2, height / 2, 0xffffffff);
} }
}; };

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,16 @@ 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.core.utils.WrappedTimer;
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 +36,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,12 +44,15 @@ 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.
*/ */
private static final List<Class> BAD_PACKETS = Arrays.<Class>asList( private static final List<Class> BAD_PACKETS = Arrays.<Class>asList(
// TODO Update possibly more? // TODO Update possibly more?
SPacketRecipeBook.class,
SPacketAdvancementInfo.class,
SPacketSelectAdvancementsTab.class,
SPacketUpdateHealth.class, SPacketUpdateHealth.class,
SPacketOpenWindow.class, SPacketOpenWindow.class,
SPacketCloseWindow.class, SPacketCloseWindow.class,
@@ -242,7 +246,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 +387,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();
@@ -404,7 +400,7 @@ public class ReplaySender extends ChannelDuplexHandler {
if (!file.exists()) { if (!file.exists()) {
IOUtils.copy(replayFile.getResourcePack(hash).get(), new FileOutputStream(file)); IOUtils.copy(replayFile.getResourcePack(hash).get(), new FileOutputStream(file));
} }
mc.getResourcePackRepository().setResourcePackInstance(file); mc.getResourcePackRepository().setServerResourcePack(file);
} }
} }
return null; return null;
@@ -464,19 +460,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.world == null || !mc.isCallingFromMinecraftThread()) { if (mc.world == 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) {
@@ -508,27 +507,14 @@ public class ReplaySender extends ChannelDuplexHandler {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void channelActive(ChannelHandlerContext ctx) throws Exception { public void channelActive(ChannelHandlerContext ctx) throws Exception {
this.ctx = ctx; this.ctx = ctx;
ctx.attr(NetworkManager.PROTOCOL_ATTRIBUTE_KEY).set(EnumConnectionState.PLAY); ctx.channel().attr(NetworkManager.PROTOCOL_ATTRIBUTE_KEY).set(EnumConnectionState.PLAY);
super.channelActive(ctx); super.channelActive(ctx);
} }
@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
} }
/** /**
@@ -536,7 +522,7 @@ public class ReplaySender extends ChannelDuplexHandler {
* @return {@code true} if it is paused, {@code false} otherwise * @return {@code true} if it is paused, {@code false} otherwise
*/ */
public boolean paused() { public boolean paused() {
return mc.timer.timerSpeed == 0; return mc.timer.tickLength == Float.POSITIVE_INFINITY;
} }
/** /**
@@ -556,7 +542,7 @@ public class ReplaySender extends ChannelDuplexHandler {
*/ */
public void setReplaySpeed(final double d) { public void setReplaySpeed(final double d) {
if(d != 0) this.replaySpeed = d; if(d != 0) this.replaySpeed = d;
mc.timer.timerSpeed = (float) d; mc.timer.tickLength = WrappedTimer.DEFAULT_MS_PER_TICK / (float) d;
} }
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
@@ -756,9 +742,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;
@@ -20,12 +19,12 @@ import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.item.EntityItemFrame; import net.minecraft.entity.item.EntityItemFrame;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.stats.RecipeBook;
import net.minecraft.stats.StatisticsManager; import net.minecraft.stats.StatisticsManager;
import net.minecraft.util.EnumHand; 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;
@@ -68,8 +67,8 @@ public class CameraEntity extends EntityPlayerSP {
*/ */
private final EventHandler eventHandler = new EventHandler(); private final EventHandler eventHandler = new EventHandler();
public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatisticsManager statisticsManager) { public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatisticsManager statisticsManager, RecipeBook recipeBook) {
super(mcIn, worldIn, netHandlerPlayClient, statisticsManager); super(mcIn, worldIn, netHandlerPlayClient, statisticsManager, recipeBook);
MinecraftForge.EVENT_BUS.register(eventHandler); MinecraftForge.EVENT_BUS.register(eventHandler);
if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) { if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) {
cameraController = ReplayModReplay.instance.createCameraController(this); cameraController = ReplayModReplay.instance.createCameraController(this);
@@ -386,12 +385,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 sendMessage(ITextComponent message) {
if (MinecraftForge.EVENT_BUS.post(new ReplayChatMessageEvent(this))) return;
super.sendMessage(message);
}
private class EventHandler { private class EventHandler {
@SubscribeEvent @SubscribeEvent
public void onPreClientTick(TickEvent.ClientTickEvent event) { public void onPreClientTick(TickEvent.ClientTickEvent event) {
@@ -468,45 +461,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

@@ -132,7 +132,7 @@ public class ClassicCameraController implements CameraController {
Vec3d movement = direction.normalize(); Vec3d movement = direction.normalize();
double factor = motion * (frac / 1000D); double factor = motion * (frac / 1000D);
camera.moveCamera(movement.xCoord * factor, movement.yCoord * factor, movement.zCoord * factor); camera.moveCamera(movement.x * factor, movement.y * factor, movement.z * factor);
} }
private void setMovement(MoveDirection dir) { private void setMovement(MoveDirection dir) {

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,11 +28,13 @@ 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;
import org.apache.logging.log4j.core.helpers.Strings; import org.apache.logging.log4j.util.Strings;
import org.lwjgl.Sys; import org.lwjgl.Sys;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
@@ -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

@@ -17,7 +17,7 @@ import java.util.List;
public class GuiHandler { public class GuiHandler {
private static final int BUTTON_EXIT_SERVER = 1; private static final int BUTTON_EXIT_SERVER = 1;
private static final int BUTTON_ACHIEVEMENTS = 5; private static final int BUTTON_ADVANCEMENTS = 5;
private static final int BUTTON_STATS = 6; private static final int BUTTON_STATS = 6;
private static final int BUTTON_OPEN_TO_LAN = 7; private static final int BUTTON_OPEN_TO_LAN = 7;
@@ -55,8 +55,8 @@ public class GuiHandler {
b.displayString = I18n.format("replaymod.gui.exit"); b.displayString = I18n.format("replaymod.gui.exit");
b.id = BUTTON_EXIT_REPLAY; b.id = BUTTON_EXIT_REPLAY;
break; break;
// Remove "Achievements", "Stats" and "Open to LAN" buttons // Remove "Advancements", "Stats" and "Open to LAN" buttons
case BUTTON_ACHIEVEMENTS: case BUTTON_ADVANCEMENTS:
buttonList.remove(achievements = b); buttonList.remove(achievements = b);
break; break;
case BUTTON_STATS: case BUTTON_STATS:
@@ -68,12 +68,12 @@ public class GuiHandler {
} }
} }
if (achievements != null && stats != null) { if (achievements != null && stats != null) {
moveAllButtonsDirectlyBelowUpwards(buttonList, achievements.yPosition, moveAllButtonsDirectlyBelowUpwards(buttonList, achievements.y,
achievements.xPosition, stats.xPosition + stats.width); achievements.x, stats.x + stats.width);
} }
if (openToLan != null) { if (openToLan != null) {
moveAllButtonsDirectlyBelowUpwards(buttonList, openToLan.yPosition, moveAllButtonsDirectlyBelowUpwards(buttonList, openToLan.y,
openToLan.xPosition, openToLan.xPosition + openToLan.width); openToLan.x, openToLan.x + openToLan.width);
} }
} }
} }
@@ -87,8 +87,8 @@ public class GuiHandler {
*/ */
private void moveAllButtonsDirectlyBelowUpwards(List<GuiButton> buttons, int belowY, int xStart, int xEnd) { private void moveAllButtonsDirectlyBelowUpwards(List<GuiButton> buttons, int belowY, int xStart, int xEnd) {
for (GuiButton button : buttons) { for (GuiButton button : buttons) {
if (button.yPosition >= belowY && button.xPosition <= xEnd && button.xPosition + button.width >= xStart) { if (button.y >= belowY && button.x <= xEnd && button.x + button.width >= xStart) {
button.yPosition -= 24; button.y -= 24;
} }
} }
} }

View File

@@ -6,6 +6,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.multiplayer.PlayerControllerMP; import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.stats.RecipeBook;
import net.minecraft.stats.StatisticsManager; import net.minecraft.stats.StatisticsManager;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -23,10 +24,10 @@ public abstract class MixinPlayerControllerMP {
@Shadow @Shadow
private NetHandlerPlayClient connection; private NetHandlerPlayClient connection;
@Inject(method = "createClientPlayer", at=@At("HEAD"), cancellable = true) @Inject(method = "func_192830_a", at=@At("HEAD"), cancellable = true)
private void replayModReplay_createReplayCamera(World worldIn, StatisticsManager statisticsManager, CallbackInfoReturnable<EntityPlayerSP> ci) { private void replayModReplay_createReplayCamera(World worldIn, StatisticsManager statisticsManager, RecipeBook recipeBook, CallbackInfoReturnable<EntityPlayerSP> ci) {
if (ReplayModReplay.instance.getReplayHandler() != null) { if (ReplayModReplay.instance.getReplayHandler() != null) {
ci.setReturnValue(new CameraEntity(mc, worldIn, connection, statisticsManager)); ci.setReturnValue(new CameraEntity(mc, worldIn, connection, statisticsManager, recipeBook));
ci.cancel(); ci.cancel();
} }
} }

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, @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

@@ -658,7 +658,7 @@ public class SPTimeline implements PathingRegistry {
} catch (IOException e) { } catch (IOException e) {
CrashReport crash = CrashReport.makeCrashReport(e, "Serializing interpolator"); CrashReport crash = CrashReport.makeCrashReport(e, "Serializing interpolator");
CrashReportCategory category = crash.makeCategory("Serializing interpolator"); CrashReportCategory category = crash.makeCategory("Serializing interpolator");
category.setDetail("Interpolator", interpolator::toString); category.addDetail("Interpolator", interpolator::toString);
throw new ReportedException(crash); throw new ReportedException(crash);
} }

View File

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

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",