Merge branch 'johni/1.13-pre' into johni/blend

This commit is contained in:
Jonas Herzig
2019-03-13 16:07:32 +01:00
177 changed files with 7973 additions and 3552 deletions

View File

@@ -27,7 +27,13 @@ public class OptifineReflection {
public static void reloadLang() {
try {
Class.forName("Lang").getDeclaredMethod("resourcesReloaded").invoke(null);
Class<?> langClass;
try {
langClass = Class.forName("Lang"); // Pre Optifine 1.12.2 E1
} catch (ClassNotFoundException ignore) {
langClass = Class.forName("net.optifine.Lang"); // Post Optifine 1.12.2 E1
}
langClass.getDeclaredMethod("resourcesReloaded").invoke(null);
} catch (ClassNotFoundException ignore) {
// no optifine installed
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {

View File

@@ -25,16 +25,19 @@ public class ShaderReflection {
static {
try {
shaders_frameTimeCounter = Class.forName("shadersmod.client.Shaders")
.getDeclaredField("frameTimeCounter");
Class<?> shadersClass;
try {
shadersClass = Class.forName("shadersmod.client.Shaders"); // Pre Optifine 1.12.2 E1
} catch (ClassNotFoundException ignore) {
shadersClass = Class.forName("net.optifine.shaders.Shaders"); // Post Optifine 1.12.2 E1
}
shaders_frameTimeCounter = shadersClass.getDeclaredField("frameTimeCounter");
shaders_frameTimeCounter.setAccessible(true);
shaders_isShadowPass = Class.forName("shadersmod.client.Shaders")
.getDeclaredField("isShadowPass");
shaders_isShadowPass = shadersClass.getDeclaredField("isShadowPass");
shaders_isShadowPass.setAccessible(true);
shaders_beginRender = Class.forName("shadersmod.client.Shaders")
.getDeclaredMethod("beginRender", Minecraft.class, float.class, long.class);
shaders_beginRender = shadersClass.getDeclaredMethod("beginRender", Minecraft.class, float.class, long.class);
shaders_beginRender.setAccessible(true);
renderGlobal_chunksToUpdateForced = Class.forName("net.minecraft.client.renderer.RenderGlobal")

View File

@@ -9,7 +9,10 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Pseudo
@Mixin(targets = "shadersmod/client/ShadersRender", remap = false)
@Mixin(targets = {
"shadersmod/client/ShadersRender", // Pre Optifine 1.12.2 E1
"net/optifine/shaders/ShadersRender" // Post Optifine 1.12.2 E1
}, remap = false)
public abstract class MixinShadersRender {
@Inject(method = "renderHand0", at = @At("HEAD"), cancellable = true)

View File

@@ -6,12 +6,19 @@ import com.replaymod.core.versions.MCVer;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.ReportedException;
import org.lwjgl.input.Keyboard;
//#if MC>=11300
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.InputEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
//#else
@@ -95,24 +102,25 @@ public class KeyBindingRegistry {
} catch (Throwable cause) {
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding");
CrashReportCategory category = crashReport.makeCategory("Key Binding");
category.addCrashSection("Key Binding", keyBinding);
MCVer.addDetail(category, "Key Binding", keyBinding::toString);
MCVer.addDetail(category, "Handler", runnable::toString);
throw new ReportedException(crashReport);
throw newReportedException(crashReport);
}
}
}
public void handleRaw() {
int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey();
//int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey();
int keyCode = -1; // FIXME
for (final Runnable runnable : rawHandlers.get(keyCode)) {
try {
runnable.run();
} catch (Throwable cause) {
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Raw Key Binding");
CrashReportCategory category = crashReport.makeCategory("Key Binding");
category.addCrashSection("Key Code", keyCode);
MCVer.addDetail(category, "Key Code", () -> "" + keyCode);
MCVer.addDetail(category, "Handler", runnable::toString);
throw new ReportedException(crashReport);
throw newReportedException(crashReport);
}
}
}

View File

@@ -1,12 +1,13 @@
package com.replaymod.core;
import org.apache.logging.log4j.LogManager;
import org.spongepowered.asm.launch.MixinBootstrap;
import org.spongepowered.asm.mixin.Mixins;
//#if MC<11300
//$$ import org.apache.logging.log4j.LogManager;
//$$ import org.spongepowered.asm.launch.MixinBootstrap;
//$$ import org.spongepowered.asm.mixin.Mixins;
//$$
//#if MC>=10800
import net.minecraftforge.fml.relauncher.CoreModManager;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
//$$ import net.minecraftforge.fml.relauncher.CoreModManager;
//$$ import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
//#else
//$$ import com.replaymod.core.asm.GLErrorTransformer;
//$$ import com.replaymod.core.asm.GLStateTrackerTransformer;
@@ -16,57 +17,57 @@ import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
//$$ import java.util.ArrayList;
//$$ import java.util.List;
//#endif
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.util.Map;
@IFMLLoadingPlugin.TransformerExclusions("com.replaymod.core.asm.")
public class LoadingPlugin implements IFMLLoadingPlugin {
public LoadingPlugin() {
MixinBootstrap.init();
Mixins.addConfiguration("mixins.recording.replaymod.json");
Mixins.addConfiguration("mixins.render.replaymod.json");
Mixins.addConfiguration("mixins.render.blend.replaymod.json");
Mixins.addConfiguration("mixins.replay.replaymod.json");
Mixins.addConfiguration("mixins.compat.mapwriter.replaymod.json");
Mixins.addConfiguration("mixins.compat.shaders.replaymod.json");
Mixins.addConfiguration("mixins.extras.playeroverview.replaymod.json");
CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
if (codeSource != null) {
URL location = codeSource.getLocation();
try {
File file = new File(location.toURI());
if (file.isFile()) {
// This forces forge to reexamine the jar file for FML mods
// Should eventually be handled by Mixin itself, maybe?
//$$
//$$ import java.io.File;
//$$ import java.net.URISyntaxException;
//$$ import java.net.URL;
//$$ import java.security.CodeSource;
//$$ import java.util.Map;
//$$
//$$ @IFMLLoadingPlugin.TransformerExclusions("com.replaymod.core.asm.")
//$$ public class LoadingPlugin implements IFMLLoadingPlugin {
//$$
//$$ public LoadingPlugin() {
//$$ MixinBootstrap.init();
//$$ Mixins.addConfiguration("mixins.recording.replaymod.json");
//$$ Mixins.addConfiguration("mixins.render.replaymod.json");
//$$ Mixins.addConfiguration("mixins.render.blend.replaymod.json");
//$$ Mixins.addConfiguration("mixins.replay.replaymod.json");
//$$ Mixins.addConfiguration("mixins.compat.mapwriter.replaymod.json");
//$$ Mixins.addConfiguration("mixins.compat.shaders.replaymod.json");
//$$ Mixins.addConfiguration("mixins.extras.playeroverview.replaymod.json");
//$$
//$$ CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
//$$ if (codeSource != null) {
//$$ URL location = codeSource.getLocation();
//$$ try {
//$$ File file = new File(location.toURI());
//$$ if (file.isFile()) {
//$$ // This forces forge to reexamine the jar file for FML mods
//$$ // Should eventually be handled by Mixin itself, maybe?
//#if MC>=10809
CoreModManager.getIgnoredMods().remove(file.getName());
//$$ CoreModManager.getIgnoredMods().remove(file.getName());
//#else
//$$ CoreModManager.getLoadedCoremods().remove(file.getName());
//#if MC<=10710
//$$ CoreModManager.getReparseableCoremods().add(file.getName());
//#endif
//#endif
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
} else {
LogManager.getLogger().warn("No CodeSource, if this is not a development environment we might run into problems!");
LogManager.getLogger().warn(getClass().getProtectionDomain());
}
}
@Override
public String[] getASMTransformerClass() {
//$$ }
//$$ } catch (URISyntaxException e) {
//$$ e.printStackTrace();
//$$ }
//$$ } else {
//$$ LogManager.getLogger().warn("No CodeSource, if this is not a development environment we might run into problems!");
//$$ LogManager.getLogger().warn(getClass().getProtectionDomain());
//$$ }
//$$ }
//$$
//$$ @Override
//$$ public String[] getASMTransformerClass() {
//#if MC>=10800
return new String[]{
};
//$$ return new String[]{
//$$ };
//#else
//$$ List<String> transformers = new ArrayList<>();
//$$ if ("true".equals(System.getProperty("replaymod.glerrors", "false"))) {
@@ -75,25 +76,26 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
//$$ transformers.add(GLStateTrackerTransformer.class.getName());
//$$ return transformers.stream().toArray(String[]::new);
//#endif
}
@Override
public String getModContainerClass() {
return null;
}
@Override
public String getSetupClass() {
return null;
}
@Override
public void injectData(Map<String, Object> data) {
}
@Override
public String getAccessTransformerClass() {
return null;
}
}
//$$ }
//$$
//$$ @Override
//$$ public String getModContainerClass() {
//$$ return null;
//$$ }
//$$
//$$ @Override
//$$ public String getSetupClass() {
//$$ return null;
//$$ }
//$$
//$$ @Override
//$$ public void injectData(Map<String, Object> data) {
//$$ }
//$$
//$$ @Override
//$$ public String getAccessTransformerClass() {
//$$ return null;
//$$ }
//$$
//$$ }
//#endif

View File

@@ -0,0 +1,10 @@
package com.replaymod.core;
public interface Module {
// FMLCommonSetupEvent for 1.13+, FMLInitializationEvent below
default void initCommon() {}
// FMLClientSetupEvent for 1.13+, FMLInitializationEvent (if client) below
default void initClient() {}
// FMLClientSetupEvent for 1.13+, FMLInitializationEvent below
default void registerKeyBindings(KeyBindingRegistry registry) {}
}

View File

@@ -2,21 +2,42 @@ package com.replaymod.core;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFutureTask;
import com.replaymod.core.gui.GuiBackgroundProcesses;
import com.replaymod.core.gui.GuiReplaySettings;
import com.replaymod.core.gui.RestoreReplayGui;
import com.replaymod.core.handler.MainMenuHandler;
import com.replaymod.core.utils.OpenGLUtils;
import com.replaymod.core.versions.MCVer;
import com.replaymod.editor.ReplayModEditor;
import com.replaymod.extras.ReplayModExtras;
import com.replaymod.online.ReplayModOnline;
import com.replaymod.recording.ReplayModRecording;
import com.replaymod.render.ReplayModRender;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replaystudio.util.I18n;
import com.replaymod.simplepathing.ReplayModSimplePathing;
import de.johni0702.minecraft.gui.container.GuiScreen;
import lombok.Getter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.FolderResourcePack;
import net.minecraft.client.resources.IResourcePack;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.config.Configuration;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils;
//#if MC>=11300
import com.replaymod.core.versions.LangResourcePack;
import net.minecraft.resources.FolderPack;
import net.minecraft.resources.IPackFinder;
import net.minecraft.resources.ResourcePackInfo;
import net.minecraftforge.fml.DeferredWorkQueue;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.versions.mcp.MCPVersion;
//#else
//$$ import net.minecraft.client.resources.FolderResourcePack;
//$$ import net.minecraft.client.resources.IResourcePack;
//$$ import net.minecraftforge.common.config.Configuration;
//#endif
//#if MC>=10904
import net.minecraft.util.text.*;
//#else
@@ -28,18 +49,23 @@ import net.minecraft.util.text.*;
//#endif
//#if MC>=10800
import net.minecraft.client.settings.GameSettings;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.Loader;
//#if MC>=11300
import net.minecraft.client.GameSettings;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
//#else
//$$ import net.minecraft.client.settings.GameSettings;
//$$ import net.minecraftforge.fml.client.FMLClientHandler;
//$$ import net.minecraftforge.fml.common.Loader;
//$$ import net.minecraftforge.fml.common.Mod.EventHandler;
//$$ import net.minecraftforge.fml.common.Mod.Instance;
//$$ import net.minecraftforge.fml.common.ModContainer;
//$$ import net.minecraftforge.fml.common.event.FMLInitializationEvent;
//$$ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.Mod.Instance;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
//#else
//$$ import cpw.mods.fml.common.Loader;
@@ -48,9 +74,7 @@ import net.minecraftforge.fml.common.gameevent.TickEvent;
//$$ import cpw.mods.fml.common.Mod.Instance;
//$$ import cpw.mods.fml.common.ModContainer;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPostInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//$$ import cpw.mods.fml.common.eventhandler.EventBus;
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//$$ import cpw.mods.fml.common.gameevent.TickEvent;
//$$ import com.replaymod.replay.InputReplayTimer;
@@ -62,48 +86,108 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.FutureTask;
import static com.replaymod.core.versions.MCVer.*;
@Mod(modid = ReplayMod.MOD_ID,
useMetadata = true,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=11300
@Mod(ReplayMod.MOD_ID)
//#else
//$$ @Mod(modid = ReplayMod.MOD_ID,
//$$ useMetadata = true,
//$$ version = "@MOD_VERSION@",
//$$ acceptedMinecraftVersions = "@MC_VERSION@",
//$$ acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
//$$ clientSideOnly = true,
//$$ updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
//#endif
guiFactory = "com.replaymod.core.gui.GuiFactory")
public class ReplayMod {
//$$ guiFactory = "com.replaymod.core.gui.GuiFactory")
//#endif
public class ReplayMod implements Module {
public static ModContainer getContainer() {
return Loader.instance().getIndexedModList().get(MOD_ID);
//#if MC>=11300
return ModList.get().getModContainerById(MOD_ID).get();
//#else
//$$ return Loader.instance().getIndexedModList().get(MOD_ID);
//#endif
}
@Getter(lazy = true)
private static final String minecraftVersion = Loader.MC_VERSION;
//#if MC>=11300
private static final String minecraftVersion = MCPVersion.getMCVersion();
//#else
//$$ private static final String minecraftVersion = Loader.MC_VERSION;
//#endif
public static final String MOD_ID = "replaymod";
public static final ResourceLocation TEXTURE = new ResourceLocation("replaymod", "replay_gui.png");
public static final int TEXTURE_SIZE = 256;
private static final Minecraft mc = Minecraft.getMinecraft();
private static final Minecraft mc = MCVer.getMinecraft();
@Deprecated
public static Configuration config;
//#if MC<11300
//$$ @Deprecated
//$$ public static Configuration config;
//#endif
private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry();
private final SettingsRegistry settingsRegistry = new SettingsRegistry();
{
settingsRegistry.register(Setting.class);
}
// The instance of your mod that Forge uses.
@Instance(MOD_ID)
//#if MC>=11300
{ instance = this; }
//#else
//$$ @Instance(MOD_ID)
//#endif
public static ReplayMod instance;
private final List<Module> modules = new ArrayList<>();
private final GuiBackgroundProcesses backgroundProcesses = new GuiBackgroundProcesses();
public ReplayMod() {
I18n.setI18n(net.minecraft.client.resources.I18n::format);
//#if MC>=11300
DeferredWorkQueue.runLater(() -> MCVer.getMinecraft().resourcePackRepository.addPackFinder(new LangResourcePack.Finder()));
//#endif
// Register all RM modules
modules.add(this);
modules.add(new ReplayModRecording(this));
ReplayModReplay replayModule = new ReplayModReplay(this);
modules.add(replayModule);
modules.add(new ReplayModOnline(this, replayModule));
modules.add(new ReplayModRender(this));
modules.add(new ReplayModSimplePathing(this));
modules.add(new ReplayModEditor(this));
modules.add(new ReplayModExtras(this));
//#if MC>=11300
settingsRegistry.register();
//#endif
}
//#if MC<=11300
//$$ @EventHandler
//$$ public void init(FMLPreInitializationEvent event) {
//$$ config = new Configuration(event.getSuggestedConfigurationFile());
//$$ config.load();
//$$ settingsRegistry.setConfiguration(config);
//$$ settingsRegistry.save(); // Save default values to disk
//$$ }
//#endif
public KeyBindingRegistry getKeyBindingRegistry() {
return keyBindingRegistry;
}
@@ -114,33 +198,28 @@ public class ReplayMod {
public File getReplayFolder() throws IOException {
String path = getSettingsRegistry().get(Setting.RECORDING_PATH);
File folder = new File(path.startsWith("./") ? getMinecraft().mcDataDir : null, path);
File folder = new File(path.startsWith("./") ? mcDataDir(getMinecraft()) : null, path);
FileUtils.forceMkdir(folder);
return folder;
}
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
// Initialize the static OpenGL info field from the minecraft main thread
// Unfortunately lwjgl uses static methods so we have to make use of magic init calls as well
OpenGLUtils.init();
I18n.setI18n(net.minecraft.client.resources.I18n::format);
config = new Configuration(event.getSuggestedConfigurationFile());
config.load();
settingsRegistry.setConfiguration(config);
}
//#ifdef DEV_ENV
static { // Note: even preInit is too late and we'd have to issue another resource reload
@SuppressWarnings("unchecked")
List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks;
FolderResourcePack jGuiResourcePack = new FolderResourcePack(new File("../jGui/src/main/resources")) {
//#if MC>=11300
FolderPack jGuiResourcePack = new FolderPack(new File("../jGui/src/main/resources")) {
@Override
protected InputStream getInputStreamByName(String resourceName) throws IOException {
protected InputStream getInputStream(String resourceName) throws IOException {
try {
return super.getInputStreamByName(resourceName);
return super.getInputStream(resourceName);
//#else
//$$ List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks;
//$$ FolderResourcePack jGuiResourcePack = new FolderResourcePack(new File("../jGui/src/main/resources")) {
//$$ @Override
//$$ protected InputStream getInputStreamByName(String resourceName) throws IOException {
//$$ try {
//$$ return super.getInputStreamByName(resourceName);
//#endif
} catch (IOException e) {
if ("pack.mcmeta".equals(resourceName)) {
return new ByteArrayInputStream(("{\"pack\": {\"description\": \"dummy pack for jGui resources in dev-env\", \"pack_format\": 1}}").getBytes(Charsets.UTF_8));
@@ -149,7 +228,16 @@ public class ReplayMod {
}
}
};
defaultResourcePacks.add(jGuiResourcePack);
//#if MC>=11300
mc.resourcePackRepository.addPackFinder(new IPackFinder() {
@Override
public <T extends ResourcePackInfo> void addPackInfosToMap(Map<String, T> map, ResourcePackInfo.IFactory<T> factory) {
map.put("jgui", ResourcePackInfo.func_195793_a("jgui", true, () -> jGuiResourcePack, factory, ResourcePackInfo.Priority.BOTTOM));
}
});
//#else
//$$ defaultResourcePacks.add(jGuiResourcePack);
//#endif
//#if MC<=10710
//$$ FolderResourcePack mainResourcePack = new FolderResourcePack(new File("../src/main/resources")) {
//$$ @Override
@@ -169,30 +257,43 @@ public class ReplayMod {
}
//#endif
@EventHandler
public void init(FMLInitializationEvent event) {
getSettingsRegistry().register(Setting.class);
//#if MC>=11300
{
FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLCommonSetupEvent event) -> modules.forEach(Module::initCommon));
FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLClientSetupEvent event) -> modules.forEach(Module::initClient));
FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLClientSetupEvent event) -> modules.forEach(m -> m.registerKeyBindings(keyBindingRegistry)));
}
//#else
//$$ @EventHandler
//$$ public void init(FMLInitializationEvent event) {
//$$ modules.forEach(Module::initCommon);
//$$ modules.forEach(Module::initClient);
//$$ modules.forEach(m -> m.registerKeyBindings(keyBindingRegistry));
//$$ }
//#endif
@Override
public void registerKeyBindings(KeyBindingRegistry registry) {
registry.registerKeyBinding("replaymod.input.settings", 0, () -> {
new GuiReplaySettings(null, settingsRegistry).display();
});
}
@Override
public void initClient() {
new MainMenuHandler().register();
//#if MC<=10710
//$$ FML_BUS.register(this); // For runLater(Runnable)
//#endif
FML_BUS.register(keyBindingRegistry);
getKeyBindingRegistry().registerKeyBinding("replaymod.input.settings", 0, () -> {
new GuiReplaySettings(null, settingsRegistry).display();
});
}
@EventHandler
public void postInit(FMLPostInitializationEvent event) throws IOException {
settingsRegistry.save(); // Save default values to disk
FORGE_BUS.register(backgroundProcesses);
// 1.7.10 crashes when render distance > 16
//#if MC>=10800
if(!FMLClientHandler.instance().hasOptifine())
GameSettings.Options.RENDER_DISTANCE.setValueMax(64f);
//if(!FMLClientHandler.instance().hasOptifine()) FIXME 1.13 update
GameSettings.Options.RENDER_DISTANCE.setValueMax(64f);
//#endif
testIfMoeshAndExitMinecraft();
@@ -240,19 +341,18 @@ public class ReplayMod {
public void runLater(Runnable runnable) {
if (mc.isCallingFromMinecraftThread() && inRunLater) {
EventBus bus = FORGE_BUS;
//#if MC>=10800
bus.register(new Object() {
FORGE_BUS.register(new Object() {
@SubscribeEvent
public void onRenderTick(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
runLater(runnable);
bus.unregister(this);
FORGE_BUS.unregister(this);
}
}
});
//#else
//$$ bus.register(new RunLaterHelper(runnable));
//$$ FORGE_BUS.register(new RunLaterHelper(runnable));
//#endif
return;
}
@@ -309,7 +409,11 @@ public class ReplayMod {
//#endif
public String getVersion() {
return getContainer().getVersion();
//#if MC>=11300
return getContainer().getModInfo().getVersion().toString();
//#else
//$$ return getContainer().getVersion();
//#endif
}
private void testIfMoeshAndExitMinecraft() {
@@ -355,4 +459,8 @@ public class ReplayMod {
mc.ingameGUI.getChatGUI().printChatMessage(text);
}
}
public GuiBackgroundProcesses getBackgroundProcesses() {
return backgroundProcesses;
}
}

View File

@@ -1,30 +1,67 @@
package com.replaymod.core;
import com.replaymod.core.events.SettingsChangedEvent;
import net.minecraft.client.resources.I18n;
import net.minecraftforge.common.config.Configuration;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
//#if MC>=11300
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
//#else
//$$ import net.minecraftforge.common.config.Configuration;
//#endif
import static com.replaymod.core.versions.MCVer.*;
public class SettingsRegistry {
private static final Object NULL_OBJECT = new Object();
private Map<SettingKey<?>, Object> settings = new ConcurrentHashMap<>();
private Configuration configuration;
//#if MC>=11300
private ForgeConfigSpec spec;
private ModConfig config;
//#else
//$$ private Configuration configuration;
//#endif
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
//#if MC>=11300
public void register() {
if (spec == null) {
ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
for (SettingKey<?> key : settings.keySet()) {
builder
.translation(key.getDisplayString())
.define(key.getCategory() + "." + key.getKey(), key.getDefault());
}
spec = builder.build();
}
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::load);
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, spec);
}
List<SettingKey<?>> keys = new ArrayList<>(settings.keySet());
settings.clear();
for (SettingKey key : keys) {
register(key);
private void load(ModConfig.Loading event) {
config = event.getConfig();
for (Map.Entry<SettingKey<?>, Object> entry : settings.entrySet()) {
SettingKey<?> key = entry.getKey();
Object value = config.getConfigData().get(key.getCategory() + "." + key.getKey());
entry.setValue(value == null ? key.getDefault() : value);
}
}
//#else
//$$ public void setConfiguration(Configuration configuration) {
//$$ this.configuration = configuration;
//$$
//$$ List<SettingKey<?>> keys = new ArrayList<>(settings.keySet());
//$$ settings.clear();
//$$ for (SettingKey key : keys) {
//$$ register(key);
//$$ }
//$$ }
//#endif
public void register(Class<?> settingsClass) {
for (Field field : settingsClass.getDeclaredFields()) {
@@ -40,23 +77,30 @@ public class SettingsRegistry {
}
public void register(SettingKey<?> key) {
Object value;
if (configuration != null) {
if (key.getDefault() instanceof Boolean) {
value = configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).getBoolean();
} else if (key.getDefault() instanceof Integer) {
value = configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).getInt();
} else if (key.getDefault() instanceof Double) {
value = configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).getDouble();
} else if (key.getDefault() instanceof String) {
value = configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).getString();
} else {
throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
}
} else {
value = NULL_OBJECT;
//#if MC>=11300
if (spec != null) {
throw new IllegalStateException("Cannot register more settings are spec has been built.");
}
settings.put(key, value);
settings.put(key, NULL_OBJECT);
//#else
//$$ Object value;
//$$ if (configuration != null) {
//$$ if (key.getDefault() instanceof Boolean) {
//$$ value = configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).getBoolean();
//$$ } else if (key.getDefault() instanceof Integer) {
//$$ value = configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).getInt();
//$$ } else if (key.getDefault() instanceof Double) {
//$$ value = configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).getDouble();
//$$ } else if (key.getDefault() instanceof String) {
//$$ value = configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).getString();
//$$ } else {
//$$ throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
//$$ }
//$$ } else {
//$$ value = NULL_OBJECT;
//$$ }
//$$ settings.put(key, value);
//#endif
}
public Set<SettingKey<?>> getSettings() {
@@ -72,23 +116,35 @@ public class SettingsRegistry {
}
public <T> void set(SettingKey<T> key, T value) {
if (key.getDefault() instanceof Boolean) {
configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).set((Boolean) value);
} else if (key.getDefault() instanceof Integer) {
configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).set((Integer) value);
} else if (key.getDefault() instanceof Double) {
configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).set((Double) value);
} else if (key.getDefault() instanceof String) {
configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).set((String) value);
} else {
throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
//#if MC>=11300
if (config != null) {
config.getConfigData().set(key.getCategory() + "." + key.getKey(), value);
}
//#else
//$$ if (key.getDefault() instanceof Boolean) {
//$$ configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).set((Boolean) value);
//$$ } else if (key.getDefault() instanceof Integer) {
//$$ configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).set((Integer) value);
//$$ } else if (key.getDefault() instanceof Double) {
//$$ configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).set((Double) value);
//$$ } else if (key.getDefault() instanceof String) {
//$$ configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).set((String) value);
//$$ } else {
//$$ throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
//$$ }
//#endif
settings.put(key, value);
FML_BUS.post(new SettingsChangedEvent(this, key));
}
public void save() {
configuration.save();
//#if MC>=11300
if (config != null) {
config.save();
}
//#else
//$$ configuration.save();
//#endif
}
public interface SettingKey<T> {
@@ -127,7 +183,7 @@ public class SettingsRegistry {
@Override
public String getDisplayString() {
return displayString == null ? null : I18n.format(displayString);
return displayString;
}
@Override

View File

@@ -5,7 +5,11 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.Event;
//#if MC>=11300
import net.minecraftforge.eventbus.api.Event;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.Event;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.Event;
//#endif

View File

@@ -0,0 +1,135 @@
package com.replaymod.core.gui;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.AbstractGuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.container.VanillaGuiScreen;
import de.johni0702.minecraft.gui.element.GuiElement;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
import static com.replaymod.core.versions.MCVer.getGui;
import static com.replaymod.core.versions.MCVer.getMinecraft;
public class GuiBackgroundProcesses {
private GuiPanel panel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(10));
public void register() {
MinecraftForge.EVENT_BUS.register(this);
}
public void unregister() {
MinecraftForge.EVENT_BUS.unregister(this);
}
@SubscribeEvent
public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) {
if (getGui(event) != getMinecraft().currentScreen) return; // people tend to construct GuiScreens without opening them
VanillaGuiScreen.setup(getGui(event)).setLayout(new CustomLayout<GuiScreen>() {
@Override
protected void layout(GuiScreen container, int width, int height) {
pos(panel, width - 5 - width(panel), 5);
}
}).addElements(null, panel);
}
public void addProcess(GuiElement<?> element) {
panel.addElements(new VerticalLayout.Data(1.0),
new Element(element));
}
public void removeProcess(GuiElement<?> element) {
for (GuiElement child : panel.getChildren()) {
if (((Element) child).inner == element) {
panel.removeElement(child);
return;
}
}
}
private static class Element extends AbstractGuiContainer<Element> {
private GuiElement inner;
Element(GuiElement inner) {
this.inner = inner;
addElements(null, inner);
setLayout(new CustomLayout<Element>() {
@Override
protected void layout(Element container, int width, int height) {
pos(inner, 10, 10);
size(inner, width - 20, height - 20);
}
});
}
@Override
public ReadableDimension getMinSize() {
ReadableDimension minSize = inner.getMinSize();
return new Dimension(minSize.getWidth() + 20, minSize.getHeight() + 20);
}
@Override
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
final int u0 = 0;
final int v0 = 39;
renderer.bindTexture(TEXTURE);
int w = size.getWidth();
int h = size.getHeight();
// Corners
renderer.drawTexturedRect(0, 0, u0, v0, 5, 5); // Top left
renderer.drawTexturedRect(w - 5, 0, u0 + 12, v0, 5, 5); // Top right
renderer.drawTexturedRect(0, h - 5, u0, v0 + 12, 5, 5); // Bottom left
renderer.drawTexturedRect(w - 5, h - 5, u0 + 12, v0 + 12, 5, 5); // Bottom right
// Top and bottom edge
for (int x = 5; x < w - 5; x += 5) {
int rx = Math.min(5, w - 5 - x);
renderer.drawTexturedRect(x, 0, u0 + 6, v0, rx, 5); // Top
renderer.drawTexturedRect(x, h - 5, u0 + 6, v0 + 12, rx, 5); // Bottom
}
// Left and right edge
for (int y = 5; y < h - 5; y += 5) {
int ry = Math.min(5, h - 5 - y);
renderer.drawTexturedRect(0, y, u0, v0 + 6, 5, ry); // Left
renderer.drawTexturedRect(w - 5, y, u0 + 12, v0 + 6, 5, ry); // Right
}
// Center
for (int x = 5; x < w - 5; x += 5) {
for (int y = 5; y < h - 5; y += 5) {
int rx = Math.min(5, w - 5 - x);
int ry = Math.min(5, h - 5 - y);
renderer.drawTexturedRect(x, y, u0 + 6, v0 + 6, rx, ry);
}
}
super.draw(renderer, size, renderInfo);
}
@Override
protected Element getThis() {
return this;
}
}
}

View File

@@ -41,7 +41,7 @@ public class GuiReplaySettings extends AbstractGuiScreen<GuiReplaySettings> {
@SuppressWarnings("unchecked")
final SettingsRegistry.SettingKey<Boolean> booleanKey = (SettingsRegistry.SettingKey<Boolean>) key;
final GuiToggleButton button = new GuiToggleButton<>().setSize(150, 20)
.setLabel(key.getDisplayString()).setSelected(settingsRegistry.get(booleanKey) ? 0 : 1)
.setI18nLabel(key.getDisplayString()).setSelected(settingsRegistry.get(booleanKey) ? 0 : 1)
.setValues(I18n.format("options.on"), I18n.format("options.off"));
element = button.onClick(new Runnable() {
@Override
@@ -60,7 +60,7 @@ public class GuiReplaySettings extends AbstractGuiScreen<GuiReplaySettings> {
for (int j = 0; j < entries.length; j++) {
Object value = values.get(j);
entries[j] = new MultipleChoiceDropdownEntry(value,
multipleChoiceKey.getDisplayString() + ": " + I18n.format(value.toString()));
I18n.format(multipleChoiceKey.getDisplayString()) + ": " + I18n.format(value.toString()));
if (currentValue.equals(value)) {
selected = j;
}

View File

@@ -43,6 +43,9 @@ public class RestoreReplayGui extends AbstractGuiScreen<RestoreReplayGui> {
yesButton.onClick(() -> {
try {
ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), null, file);
// Commit all not-yet-committed files into the main zip file.
// If we don't do this, then re-writing packet data below can actually overwrite uncommitted packet data!
replayFile.save();
ReplayMetaData metaData = replayFile.getMetaData();
if (metaData != null && metaData.getDuration() == 0) {
// Try to restore replay duration

View File

@@ -1,5 +1,6 @@
package com.replaymod.core.handler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
@@ -9,13 +10,15 @@ import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.opengl.GL11;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
import java.io.IOException;
import static com.replaymod.core.versions.MCVer.*;
/**
@@ -37,17 +40,18 @@ public class MainMenuHandler {
if (x(button) + button.width < gui.width / 2 - 100
|| x(button) > gui.width / 2 + 100
|| y(button) > gui.height / 4 + 10 + 4 * 24) continue;
// Move button up to make space for two rows of buttons
// Move button up to make space for one rows of buttons
// and then move back down by 10 to compensate for the space to the exit button that was already there
int offset = -2 * 24 + 10;
int offset = -1 * 24 + 10;
y(button, y(button) + offset);
//#if MC>=11202
if (button == gui.realmsButton) {
//#if MC>=11300
if (button.id == 14) {
realmsOffset = offset;
}
//#endif
}
//#if MC>=11202
//#if MC>=11300
if (realmsOffset != 0 && gui.realmsNotification instanceof GuiScreenRealmsProxy) {
gui.realmsNotification = new RealmsNotificationProxy((GuiScreenRealmsProxy) gui.realmsNotification, realmsOffset);
}
@@ -55,7 +59,7 @@ public class MainMenuHandler {
}
}
//#if MC>=11202
//#if MC>=11300
private static class RealmsNotificationProxy extends GuiScreen {
private final GuiScreenRealmsProxy proxy;
private final int offset;
@@ -66,30 +70,25 @@ public class MainMenuHandler {
}
@Override
public void setGuiSize(int w, int h) {
proxy.setGuiSize(w, h);
public void setWorldAndResolution(Minecraft mc, int width, int height) {
proxy.setWorldAndResolution(mc, width, height);
}
@Override
public void initGui() {
proxy.initGui();
public void tick() {
proxy.tick();
}
@Override
public void updateScreen() {
proxy.updateScreen();
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
public void render(int mouseX, int mouseY, float partialTicks) {
GL11.glTranslated(0, offset, 0);
proxy.drawScreen(mouseX, mouseY - offset, partialTicks);
proxy.render(mouseX, mouseY - offset, partialTicks);
GL11.glTranslated(0, -offset, 0);
}
@Override
public void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
proxy.mouseClicked(mouseX, mouseY - offset, mouseButton);
public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) {
return proxy.mouseClicked(mouseX, mouseY - offset, mouseButton);
}
@Override

View File

@@ -11,11 +11,16 @@ import net.minecraftforge.registries.RegistryManager;
//#else
//$$ import cpw.mods.fml.common.registry.GameData;
//#endif
//$$ import java.util.stream.Stream;
//#endif
//#if MC>=10800
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
//#if MC>=11300
import net.minecraftforge.fml.ModList;
//#else
//$$ import net.minecraftforge.fml.common.Loader;
//$$ import net.minecraftforge.fml.common.ModContainer;
//#endif
//#else
//$$ import cpw.mods.fml.common.Loader;
//$$ import cpw.mods.fml.common.ModContainer;
@@ -24,22 +29,32 @@ import net.minecraftforge.fml.common.ModContainer;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ModCompat {
@SuppressWarnings("unchecked")
public static Collection<ModInfo> getInstalledNetworkMods() {
Map<String, ModContainer> ignoreCaseMap = Loader.instance().getModList().stream()
.collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity()));
//#if MC>=11200
//#if MC>=11300
Map<String, ModInfo> modInfoMap = ModList.get().getMods().stream()
.map(m -> new ModInfo(m.getModId(), m.getDisplayName(), m.getVersion().toString()))
.collect(Collectors.toMap(ModInfo::getId, Function.identity()));
return RegistryManager.ACTIVE.takeSnapshot(false).keySet().stream()
.map(RegistryManager.ACTIVE::getRegistry)
.map(ForgeRegistry::getKeys).flatMap(Set::stream)
.map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct()
.map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null)
.map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion()))
.map(ResourceLocation::getNamespace).filter(s -> !s.equals("minecraft")).distinct()
.map(modInfoMap::get).filter(Objects::nonNull)
.collect(Collectors.toList());
//#else
//$$ Map<String, ModContainer> ignoreCaseMap = Loader.instance().getModList().stream()
//$$ .collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity()));
//#if MC>=11200
//$$ return RegistryManager.ACTIVE.takeSnapshot(false).keySet().stream()
//$$ .map(RegistryManager.ACTIVE::getRegistry)
//$$ .map(ForgeRegistry::getKeys).flatMap(Set::stream)
//$$ .map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct()
//$$ .map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null)
//$$ .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion()))
//$$ .collect(Collectors.toList());
//#else
//#if MC>=10800
//$$ return Stream.concat(
//$$ ((Set<ResourceLocation>) GameData.getBlockRegistry().getKeys()).stream(),
@@ -61,6 +76,7 @@ public class ModCompat {
//$$ .collect(Collectors.toList());
//#endif
//#endif
//#endif
}
public static final class ModInfoDifference {

View File

@@ -1,26 +0,0 @@
package com.replaymod.core.utils;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.nio.IntBuffer;
public class OpenGLUtils {
public static final int VIEWPORT_MAX_WIDTH;
public static final int VIEWPORT_MAX_HEIGHT;
static {
IntBuffer buffer = BufferUtils.createIntBuffer(16);
GL11.glGetInteger(GL11.GL_MAX_VIEWPORT_DIMS, buffer);
VIEWPORT_MAX_WIDTH = buffer.get();
VIEWPORT_MAX_HEIGHT = buffer.get();
}
/**
* Magic init method which has to be called from the OpenGL thread so the variables in this class
* can be initialized successfully.
* Does not perform any work on its own.
*/
public static void init() {
}
}

View File

@@ -3,6 +3,7 @@ package com.replaymod.core.utils;
import net.minecraft.network.PacketBuffer;
//#if MC>=10904
import net.minecraft.network.play.server.SPacketCustomPayload;
import net.minecraft.util.ResourceLocation;
//#else
//$$ import net.minecraft.network.play.server.S3FPacketCustomPayload;
//#endif
@@ -18,7 +19,11 @@ import static com.replaymod.core.versions.MCVer.readString;
* @see <a href="https://gist.github.com/Johni0702/2547c463e51f65f312cb">Replay Restrictions Gist</a>
*/
public class Restrictions {
public static final String PLUGIN_CHANNEL = "Replay|Restrict";
//#if MC>=11300
public static final ResourceLocation PLUGIN_CHANNEL = new ResourceLocation("replaymod", "restrict");
//#else
//$$ public static final String PLUGIN_CHANNEL = "Replay|Restrict";
//#endif
private boolean noXray;
private boolean noNoclip;
private boolean onlyFirstPerson;

View File

@@ -18,15 +18,20 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import de.johni0702.minecraft.gui.versions.MCVer;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
//#if MC>=11300
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800
import net.minecraft.client.network.NetworkPlayerInfo;
@@ -64,16 +69,30 @@ import java.util.Date;
import java.util.UUID;
import java.util.function.Consumer;
import static net.minecraft.client.Minecraft.getMinecraft;
import static com.replaymod.core.versions.MCVer.Minecraft_mcDataDir;
import static com.replaymod.core.versions.MCVer.getMinecraft;
public class Utils {
private static InputStream getResourceAsStream(String path) {
// FIXME this seems broken in 1.13, hence the workaround. probably want to open an issue with modlauncher (or forge?)
//#ifdef DEV_ENV
try {
return new java.io.FileInputStream(new File("../src/main/resources" + path));
} catch (java.io.FileNotFoundException e) {
throw new RuntimeException(e);
}
//#else
//$$ return Utils.class.getResourceAsStream(path);
//#endif
}
public static final BufferedImage DEFAULT_THUMBNAIL;
static {
BufferedImage thumbnail;
try {
thumbnail = ImageIO.read(Utils.class.getClassLoader().getResourceAsStream("default_thumb.jpg"));
thumbnail = ImageIO.read(getResourceAsStream("/default_thumb.jpg"));
} catch (Exception e) {
thumbnail = new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR);
e.printStackTrace();
@@ -98,7 +117,7 @@ public class Utils {
static {
// Largely from https://community.letsencrypt.org/t/134/37
try (InputStream in = Utils.class.getResourceAsStream("/dst_root_ca_x3.pem")){
try (InputStream in = getResourceAsStream("/dst_root_ca_x3.pem")){
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(in);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
@@ -189,7 +208,11 @@ public class Utils {
}
public static boolean isCtrlDown() {
return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL);
//#if MC>=11300
return GuiScreen.isCtrlKeyDown();
//#else
//$$ return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL);
//#endif
}
public static <T> void addCallback(ListenableFuture<T> future, Consumer<T> onSuccess, Consumer<Throwable> onFailure) {
@@ -216,7 +239,7 @@ public class Utils {
// Try to save the crash report
if (crashReport.getFile() == null) {
try {
File folder = new File(getMinecraft().mcDataDir, "crash-reports");
File folder = new File(Minecraft_mcDataDir(getMinecraft()), "crash-reports");
File file = new File(folder, "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt");
logger.debug("Saving crash report to file: {}", file);
crashReport.saveToFile(file);
@@ -263,7 +286,7 @@ public class Utils {
// Replace close button with panel containing close and copy buttons
GuiButton copyToClipboardButton = new GuiButton().setI18nLabel("chat.copy").onClick(() ->
GuiScreen.setClipboardString(crashReport)).setSize(150, 20);
MCVer.setClipboardString(crashReport)).setSize(150, 20);
GuiButton closeButton = getCloseButton();
popup.removeElement(closeButton);
popup.addElements(new VerticalLayout.Data(1),
@@ -281,4 +304,17 @@ public class Utils {
super.draw(renderer, size, renderInfo);
}
}
public static <T extends Throwable> void throwIfInstanceOf(Throwable t, Class<T> cls) throws T {
if (cls.isInstance(t)) {
throw cls.cast(t);
}
}
public static void throwIfUnchecked(Throwable t) {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof Error) {
throw (Error) t;
}
}
}

View File

@@ -8,15 +8,27 @@ public class WrappedTimer extends Timer {
protected final Timer wrapped;
public WrappedTimer(Timer wrapped) {
super(0);
//#if MC>=11300
super(0, 0);
//#else
//$$ super(0);
//#endif
this.wrapped = wrapped;
copy(wrapped, this);
}
@Override
public void updateTimer() {
public void updateTimer(
//#if MC>=11300
long sysClock
//#endif
) {
copy(this, wrapped);
wrapped.updateTimer();
wrapped.updateTimer(
//#if MC>=11300
sysClock
//#endif
);
copy(wrapped, this);
}

View File

@@ -0,0 +1,146 @@
//#if MC>=11300
package com.replaymod.core.versions;
import com.google.gson.Gson;
import com.replaymod.core.ReplayMod;
import net.minecraft.resources.AbstractResourcePack;
import net.minecraft.resources.IPackFinder;
import net.minecraft.resources.IResourcePack;
import net.minecraft.resources.ResourcePackFileNotFoundException;
import net.minecraft.resources.ResourcePackInfo;
import net.minecraft.resources.ResourcePackType;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.packs.ModFileResourcePack;
import net.minecraftforge.fml.packs.ResourcePackLoader;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Resource pack which on-the-fly converts pre-1.13 language files into 1.13 json format.
*/
public class LangResourcePack extends AbstractResourcePack {
private static final Gson GSON = new Gson();
private static final String NAME = "replaymod_lang";
private static final Pattern JSON_FILE_PATTERN = Pattern.compile("^assets/" + ReplayMod.MOD_ID + "/lang/([a-z][a-z])_([a-z][a-z]).json$");
private static final Pattern LANG_FILE_NAME_PATTERN = Pattern.compile("^([a-z][a-z])_([a-z][a-z]).lang$");
LangResourcePack() {
super(new File(NAME));
}
private ModFileResourcePack getParent() {
return ResourcePackLoader.getResourcePackFor(ReplayMod.MOD_ID).orElseThrow(() -> new RuntimeException("Failed to get ReplayMod resource pack!"));
}
private String langName(String path) {
Matcher matcher = JSON_FILE_PATTERN.matcher(path);
if (!matcher.matches()) return null;
return String.format("%s_%s.lang", matcher.group(1), matcher.group(2).toUpperCase());
}
private Path langPath(String path) {
ModFileResourcePack parent = getParent();
if (parent == null) return null;
ModFile modFile = parent.getModFile();
String langName = langName(path);
if (langName == null) return null;
return modFile.getLocator().findPath(modFile, "assets", ReplayMod.MOD_ID, "lang", langName);
}
private String convertValue(String value) {
return value;
}
@Override
protected InputStream getInputStream(String path) throws IOException {
if ("pack.mcmeta".equals(path)) {
return new StringBufferInputStream("{\"pack\": {\"description\": \"ReplayMod language files\", \"pack_format\": 4}}");
}
Path langPath = langPath(path);
if (langPath == null) throw new ResourcePackFileNotFoundException(file, path);
String langFile;
try (InputStream in = Files.newInputStream(langPath)) {
langFile = IOUtils.toString(in);
}
Map<String, String> properties = new HashMap<>();
for (String line : langFile.split("\n")) {
if (line.trim().isEmpty() || line.trim().startsWith("#")) continue;
int i = line.indexOf('=');
String key = line.substring(0, i);
String value = line.substring(i + 1);
value = convertValue(value);
properties.put(key, value);
}
return new ByteArrayInputStream(GSON.toJson(properties).getBytes(StandardCharsets.UTF_8));
}
@Override
protected boolean resourceExists(String path) {
Path langPath = langPath(path);
return langPath != null && Files.exists(langPath);
}
@Override
public Collection<ResourceLocation> getAllResourceLocations(ResourcePackType resourcePackType, String path, int maxDepth, Predicate<String> filter) {
if (resourcePackType == ResourcePackType.CLIENT_RESOURCES && "lang".equals(path)) {
IResourcePack parent = getParent();
if (parent == null) return Collections.emptyList();
return parent.getAllResourceLocations(resourcePackType, path, maxDepth, name -> {
Matcher matcher = LANG_FILE_NAME_PATTERN.matcher(name);
if (matcher.matches()) {
return filter.test(String.format("%s_%s.json", matcher.group(1), matcher.group(1)));
} else {
return false;
}
}).stream().map(resourceLocation -> {
String p = resourceLocation.getPath().substring(0, "assets/lang/xx_XX".length()) + ".json";
return new ResourceLocation(resourceLocation.getNamespace(), p);
}).collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}
@Override
public Set<String> getResourceNamespaces(ResourcePackType resourcePackType) {
if (resourcePackType == ResourcePackType.CLIENT_RESOURCES) {
return Collections.singleton("replaymod");
} else {
return Collections.emptySet();
}
}
@Override
public void close() {}
public static class Finder implements IPackFinder {
@Override
public <T extends ResourcePackInfo> void addPackInfosToMap(Map<String, T> packList, ResourcePackInfo.IFactory<T> factory) {
packList.put(NAME, ResourcePackInfo.func_195793_a(NAME, true, LangResourcePack::new, factory, ResourcePackInfo.Priority.BOTTOM));
}
}
}
//#endif

View File

@@ -6,9 +6,6 @@ import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.model.ModelBox;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.multiplayer.ServerList;
import net.minecraft.client.multiplayer.WorldClient;
@@ -16,8 +13,8 @@ import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.resources.ResourcePackRepository;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
@@ -25,6 +22,7 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Util;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.client.event.GuiScreenEvent;
@@ -32,6 +30,25 @@ import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=11300
import net.minecraft.client.MainWindow;
import net.minecraft.client.renderer.entity.model.ModelBox;
import net.minecraft.client.renderer.entity.model.ModelRenderer;
import net.minecraft.client.util.InputMappings;
import net.minecraft.crash.ReportedException;
import org.lwjgl.glfw.GLFW;
//#else
//$$ import net.minecraft.client.gui.ScaledResolution;
//$$ import net.minecraft.client.model.ModelBox;
//$$ import net.minecraft.client.model.ModelRenderer;
//$$ import net.minecraft.client.resources.ResourcePackRepository;
//$$ import net.minecraft.util.ReportedException;
//$$ import org.apache.logging.log4j.LogManager;
//$$ import org.lwjgl.Sys;
//$$ import java.awt.Desktop;
//#endif
//#if MC>=10904
//#if MC>=11200
import net.minecraft.client.renderer.BufferBuilder;
@@ -63,7 +80,11 @@ import net.minecraft.client.renderer.GlStateManager.BooleanState;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.world.WorldType;
import net.minecraftforge.fml.common.eventhandler.EventBus;
//#if MC>=11300
import net.minecraftforge.eventbus.api.IEventBus;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.EventBus;
//#endif
//#else
//$$ import com.google.common.util.concurrent.Futures;
//$$ import com.replaymod.render.hooks.GLStateTracker;
@@ -85,12 +106,17 @@ import java.util.concurrent.Callable;
* Abstraction over things that have changed between different MC versions.
*/
public class MCVer {
public static EventBus FORGE_BUS = MinecraftForge.EVENT_BUS;
//#if MC>=11300
public static IEventBus FORGE_BUS = MinecraftForge.EVENT_BUS;
public static IEventBus FML_BUS = FORGE_BUS;
//#else
//$$ public static EventBus FORGE_BUS = MinecraftForge.EVENT_BUS;
//#if MC>=10809
public static EventBus FML_BUS = FORGE_BUS;
//$$ public static EventBus FML_BUS = FORGE_BUS;
//#else
//$$ public static EventBus FML_BUS = FMLCommonHandler.instance().bus();
//#endif
//#endif
public static void addDetail(CrashReportCategory category, String name, Callable<String> callable) {
//#if MC>=10904
@@ -104,6 +130,22 @@ public class MCVer {
//#endif
}
public static void addButton(GuiScreenEvent.InitGuiEvent event, GuiButton button) {
//#if MC>=11300
event.addButton(button);
//#else
//$$ getButtonList(event).add(button);
//#endif
}
public static void removeButton(GuiScreenEvent.InitGuiEvent event, GuiButton button) {
//#if MC>=11300
event.removeButton(button);
//#else
//$$ getButtonList(event).remove(button);
//#endif
}
@SuppressWarnings("unchecked")
public static List<GuiButton> getButtonList(GuiScreenEvent.InitGuiEvent event) {
//#if MC>=10904
@@ -226,6 +268,14 @@ public class MCVer {
//#endif
//#endif
public static File mcDataDir(Minecraft mc) {
//#if MC>=11300
return mc.gameDir;
//#else
//$$ return mc.mcDataDir;
//#endif
}
//#if MC>=10800
public static Entity getRenderViewEntity(Minecraft mc) {
return mc.getRenderViewEntity();
@@ -318,8 +368,11 @@ public class MCVer {
}
public static BooleanState fog() {
//#if MC>=11300
return GlStateManager.FOG.fog;
//#else
//#if MC>=10809
return GlStateManager.fogState.fog;
//$$ return GlStateManager.fogState.fog;
//#else
//#if MC>=10800
//$$ return GlStateManager.fogState.field_179049_a;
@@ -327,11 +380,15 @@ public class MCVer {
//$$ return GLStateTracker.getInstance().fog;
//#endif
//#endif
//#endif
}
public static void fog(BooleanState fog) {
//#if MC>=11300
GlStateManager.FOG.fog = fog;
//#else
//#if MC>=10809
GlStateManager.fogState.fog = fog;
//$$ GlStateManager.fogState.fog = fog;
//#else
//#if MC>=10800
//$$ GlStateManager.fogState.field_179049_a = fog;
@@ -339,22 +396,31 @@ public class MCVer {
//$$ GLStateTracker.getInstance().fog = fog;
//#endif
//#endif
//#endif
}
public static BooleanState texture2DState(int index) {
//#if MC>=11300
return GlStateManager.TEXTURES[index].texture2DState;
//#else
//#if MC>=10800
return GlStateManager.textureState[index].texture2DState;
//$$ return GlStateManager.textureState[index].texture2DState;
//#else
//$$ return GLStateTracker.getInstance().texture[index];
//#endif
//#endif
}
public static void texture2DState(int index, BooleanState texture2DState) {
//#if MC>=11300
GlStateManager.TEXTURES[index].texture2DState = texture2DState;
//#else
//#if MC>=10800
GlStateManager.textureState[index].texture2DState = texture2DState;
//$$ GlStateManager.textureState[index].texture2DState = texture2DState;
//#else
//$$ GLStateTracker.getInstance().texture[index] = texture2DState;
//#endif
//#endif
}
public static void ServerList_saveSingleServer(ServerData serverData) {
@@ -373,18 +439,29 @@ public class MCVer {
//#endif
}
public static ScaledResolution newScaledResolution(Minecraft mc) {
//#if MC>=10809
return new ScaledResolution(mc);
//#else
//$$ return new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
//#endif
}
public static ListenableFuture setServerResourcePack(ResourcePackRepository repo, File file) {
//#if MC>=11300
public static MainWindow newScaledResolution(Minecraft mc) {
return mc.mainWindow;
}
//#else
//$$ public static ScaledResolution newScaledResolution(Minecraft mc) {
//#if MC>=10809
//$$ return new ScaledResolution(mc);
//#else
//$$ return new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
//#endif
//$$ }
//#endif
public static ListenableFuture setServerResourcePack(File file) {
//#if MC>=11300
return getMinecraft().getPackFinder().func_195741_a(file);
//#else
//$$ ResourcePackRepository repo = getMinecraft().getResourcePackRepository();
//#if MC>=10809
//#if MC>=11200
return repo.setServerResourcePack(file);
//$$ return repo.setServerResourcePack(file);
//#else
//$$ return repo.setResourcePackInstance(file);
//#endif
@@ -398,6 +475,7 @@ public class MCVer {
//$$ return Futures.immediateFuture(null);
//#endif
//#endif
//#endif
}
public static boolean isKeyDown(KeyBinding keyBinding) {
@@ -566,12 +644,36 @@ public class MCVer {
public static RenderManager getRenderManager() {
//#if MC>=10800
return Minecraft.getMinecraft().getRenderManager();
return getMinecraft().getRenderManager();
//#else
//$$ return RenderManager.instance;
//#endif
}
public static Minecraft getMinecraft() {
//#if MC>=11300
return Minecraft.getInstance();
//#else
//$$ return Minecraft.getMinecraft();
//#endif
}
public static long milliTime() {
//#if MC>=11300
return Util.milliTime();
//#else
//$$ return Minecraft.getSystemTime();
//#endif
}
public static File Minecraft_mcDataDir(Minecraft mc) {
//#if MC>=11300
return mc.gameDir;
//#else
//$$ return mc.mcDataDir;
//#endif
}
public static int floor(double val) {
//#if MC>=11102
return MathHelper.floor(val);
@@ -580,6 +682,14 @@ public class MCVer {
//#endif
}
public static void bindTexture(ResourceLocation texture) {
//#if MC>=11300
getMinecraft().getTextureManager().bindTexture(texture);
//#else
//$$ getMinecraft().renderEngine.bindTexture(texture);
//#endif
}
public static float cos(float val) {
return MathHelper.cos(val);
}
@@ -612,6 +722,40 @@ public class MCVer {
//#endif
}
public static ReportedException newReportedException(CrashReport crashReport) {
return new ReportedException(crashReport);
}
public static void openFile(File file) {
//#if MC>=11300
Util.getOSType().openFile(file);
//#else
//$$ String path = file.getAbsolutePath();
//$$
//$$ // First try OS specific methods
//$$ try {
//$$ switch (Util.getOSType()) {
//$$ case WINDOWS:
//$$ Runtime.getRuntime().exec(String.format("cmd.exe /C start \"Open file\" \"%s\"", path));
//$$ return;
//$$ case OSX:
//$$ Runtime.getRuntime().exec(new String[]{"/usr/bin/open", path});
//$$ return;
//$$ }
//$$ } catch (IOException e) {
//$$ LogManager.getLogger().error("Cannot open file", e);
//$$ }
//$$
//$$ // Otherwise try to java way
//$$ try {
//$$ Desktop.getDesktop().browse(file.toURI());
//$$ } catch (Throwable throwable) {
//$$ // And if all fails, lwjgl
//$$ Sys.openURL("file://" + path);
//$$ }
//#endif
}
//#if MC<=10710
//$$ public static class GlStateManager {
//$$ public static void resetColor() { /* nop */ }
@@ -628,4 +772,59 @@ public class MCVer {
//$$ public static void rotate(float angle, float x, float y, float z) { glRotatef(angle, x, y, z); }
//$$ }
//#endif
//#if MC>=11300
public static void color(float r, float g, float b, float a) { GlStateManager.color4f(r, g, b, a); }
public static void enableAlpha() { GlStateManager.enableAlphaTest(); }
public static void disableAlpha() { GlStateManager.disableAlphaTest(); }
public static void tryBlendFuncSeparate(int l, int r, int vl, int vr) { GlStateManager.blendFuncSeparate(l, r, vl, vr); }
public static void colorLogicOp(int op) { GlStateManager.logicOp(op); }
public static abstract class Keyboard {
public static final int KEY_LCONTROL = GLFW.GLFW_KEY_LEFT_CONTROL;
public static final int KEY_LSHIFT = GLFW.GLFW_KEY_LEFT_SHIFT;
public static final int KEY_ESCAPE = GLFW.GLFW_KEY_ESCAPE;
public static final int KEY_HOME = GLFW.GLFW_KEY_HOME;
public static final int KEY_END = GLFW.GLFW_KEY_END;
public static final int KEY_UP = GLFW.GLFW_KEY_UP;
public static final int KEY_DOWN = GLFW.GLFW_KEY_DOWN;
public static final int KEY_LEFT = GLFW.GLFW_KEY_LEFT;
public static final int KEY_RIGHT = GLFW.GLFW_KEY_RIGHT;
public static final int KEY_BACK = GLFW.GLFW_KEY_BACKSPACE;
public static final int KEY_DELETE = GLFW.GLFW_KEY_DELETE;
public static final int KEY_RETURN = GLFW.GLFW_KEY_ENTER;
public static final int KEY_TAB = GLFW.GLFW_KEY_TAB;
public static final int KEY_F1 = GLFW.GLFW_KEY_F1;
public static final int KEY_A = GLFW.GLFW_KEY_A;
public static final int KEY_B = GLFW.GLFW_KEY_B;
public static final int KEY_C = GLFW.GLFW_KEY_C;
public static final int KEY_D = GLFW.GLFW_KEY_D;
public static final int KEY_E = GLFW.GLFW_KEY_E;
public static final int KEY_F = GLFW.GLFW_KEY_F;
public static final int KEY_G = GLFW.GLFW_KEY_G;
public static final int KEY_H = GLFW.GLFW_KEY_H;
public static final int KEY_I = GLFW.GLFW_KEY_I;
public static final int KEY_J = GLFW.GLFW_KEY_J;
public static final int KEY_K = GLFW.GLFW_KEY_K;
public static final int KEY_L = GLFW.GLFW_KEY_L;
public static final int KEY_M = GLFW.GLFW_KEY_M;
public static final int KEY_N = GLFW.GLFW_KEY_N;
public static final int KEY_O = GLFW.GLFW_KEY_O;
public static final int KEY_P = GLFW.GLFW_KEY_P;
public static final int KEY_Q = GLFW.GLFW_KEY_Q;
public static final int KEY_R = GLFW.GLFW_KEY_R;
public static final int KEY_S = GLFW.GLFW_KEY_S;
public static final int KEY_T = GLFW.GLFW_KEY_T;
public static final int KEY_U = GLFW.GLFW_KEY_U;
public static final int KEY_V = GLFW.GLFW_KEY_V;
public static final int KEY_W = GLFW.GLFW_KEY_W;
public static final int KEY_X = GLFW.GLFW_KEY_X;
public static final int KEY_Y = GLFW.GLFW_KEY_Y;
public static final int KEY_Z = GLFW.GLFW_KEY_Z;
public static boolean isKeyDown(int keyCode) {
return InputMappings.isKeyDown(keyCode);
}
}
//#endif
}

View File

@@ -1,49 +1,29 @@
package com.replaymod.editor;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod;
import com.replaymod.editor.handler.GuiHandler;
import com.replaymod.online.Setting;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if MC>=10800
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
//#else
//$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//#endif
@Mod(modid = ReplayModEditor.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModEditor {
public static final String MOD_ID = "replaymod-editor";
@Mod.Instance(MOD_ID)
public class ReplayModEditor implements Module {
{ instance = this; }
public static ReplayModEditor instance;
private ReplayMod core;
public static Logger LOGGER;
public static Logger LOGGER = LogManager.getLogger();
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
ReplayModEditor.LOGGER = event.getModLog();
core = ReplayMod.instance;
public ReplayModEditor(ReplayMod core) {
this.core = core;
core.getSettingsRegistry().register(Setting.class);
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
new GuiHandler(this).register();
@Override
public void initClient() {
new GuiHandler().register();
}
public ReplayMod getCore() {

View File

@@ -0,0 +1,262 @@
package com.replaymod.editor.gui;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.editor.ReplayModEditor;
import com.replaymod.replay.gui.overlay.GuiMarkerTimeline;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiHorizontalScrollbar;
import de.johni0702.minecraft.gui.element.GuiTexturedButton;
import de.johni0702.minecraft.gui.element.GuiTooltip;
import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
import de.johni0702.minecraft.gui.element.advanced.GuiTimelineTime;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.utils.lwjgl.Color;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class GuiEditReplay extends AbstractGuiPopup<GuiEditReplay> {
private final Path inputPath;
private final EditTimeline timeline;
private final GuiTimelineTime<GuiMarkerTimeline> timelineTime = new GuiTimelineTime<>();
private final GuiHorizontalScrollbar scrollbar = new GuiHorizontalScrollbar().setSize(300, 9);
private final GuiTexturedButton zoomInButton = new GuiTexturedButton().setSize(9, 9)
.onClick(() -> zoomTimeline(2d / 3d))
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20)
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomin"));
private final GuiTexturedButton zoomOutButton = new GuiTexturedButton().setSize(9, 9)
.onClick(() -> zoomTimeline(3d / 2d))
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30)
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomout"));
private final GuiPanel zoomButtonPanel = new GuiPanel()
.setLayout(new VerticalLayout(VerticalLayout.Alignment.CENTER).setSpacing(2))
.addElements(null, zoomInButton, zoomOutButton);
private Set<Marker> markers;
protected GuiEditReplay(GuiContainer container, Path inputPath) throws IOException {
super(container);
this.inputPath = inputPath;
try (ZipReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), inputPath.toFile())) {
markers = replayFile.getMarkers().or(HashSet::new);
timeline = new EditTimeline(new HashSet<>(markers), markers -> this.markers = markers);
timeline.setSize(300, 20)
.setMarkers()
.setLength(replayFile.getMetaData().getDuration())
.onClick(timeline::setCursorPosition);
}
timelineTime.setTimeline(timeline);
scrollbar.onValueChanged(() -> {
timeline.setOffset((int) (scrollbar.getPosition() * timeline.getLength()));
timeline.setZoom(scrollbar.getZoom());
}).setZoom(1);
GuiPanel timelinePanel = new GuiPanel()
.setSize(300, 40)
.setLayout(new CustomLayout<GuiPanel>() {
@Override
protected void layout(GuiPanel container, int width, int height) {
pos(zoomButtonPanel, width - width(zoomButtonPanel), 10);
pos(timelineTime, 0, 2);
size(timelineTime, x(zoomButtonPanel), 8);
pos(timeline, 0, y(timelineTime) + height(timelineTime));
size(timeline, x(zoomButtonPanel) - 2, 20);
pos(scrollbar, 0, y(timeline) + height(timeline) + 1);
size(scrollbar, x(zoomButtonPanel) - 2, 9);
}
})
.addElements(null, timelineTime, timeline, scrollbar, zoomButtonPanel);
GuiButton buttonAddSplit = new GuiButton()
.setSize(100, 20)
.setI18nLabel("replaymod.gui.edit.split")
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.edit.split.tooltip"))
.onClick(() -> {
Marker marker = new Marker();
marker.setTime(timeline.getCursorPosition());
marker.setName(MarkerProcessor.MARKER_NAME_SPLIT);
timeline.addMarker(marker);
});
GuiButton buttonInsertCut = new GuiButton()
.setSize(100, 20)
.setI18nLabel("replaymod.gui.edit.cut.start")
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.edit.cut.tooltip"))
.onClick(() -> {
Marker marker = new Marker();
marker.setTime(timeline.getCursorPosition());
marker.setName(MarkerProcessor.MARKER_NAME_START_CUT);
timeline.addMarker(marker);
});
GuiButton buttonEndCut = new GuiButton()
.setSize(100, 20)
.setI18nLabel("replaymod.gui.edit.cut.end")
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.edit.cut.tooltip"))
.onClick(() -> {
Marker marker = new Marker();
marker.setTime(timeline.getCursorPosition());
marker.setName(MarkerProcessor.MARKER_NAME_END_CUT);
timeline.addMarker(marker);
});
GuiPanel controlPanel = new GuiPanel()
.setLayout(new HorizontalLayout().setSpacing(4))
.addElements(null, buttonAddSplit, buttonInsertCut, buttonEndCut);
GuiButton applyButton = new GuiButton().setI18nLabel("replaymod.gui.edit.apply").setSize(150, 20).onClick(this::apply);
GuiButton closeButton = new GuiButton().setI18nLabel("replaymod.gui.close").setSize(150, 20).onClick(this::close);
GuiPanel buttonPanel = new GuiPanel()
.setLayout(new HorizontalLayout().setSpacing(8))
.addElements(null, applyButton, closeButton);
popup.setLayout(new VerticalLayout(VerticalLayout.Alignment.TOP).setSpacing(10));
popup.addElements(new VerticalLayout.Data(0.5), timelinePanel, controlPanel, buttonPanel);
}
private void zoomTimeline(double factor) {
scrollbar.setZoom(scrollbar.getZoom() * factor);
}
private void apply() {
ProgressPopup progressPopup = new ProgressPopup(this);
new Thread(() -> {
try (ZipReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), inputPath.toFile())) {
replayFile.writeMarkers(markers);
replayFile.save();
} catch (IOException e) {
Utils.error(ReplayModEditor.LOGGER, this, CrashReport.makeCrashReport(e, "Writing markers"), this::close);
}
try {
MarkerProcessor.apply(inputPath, progressPopup.progressBar::setProgress);
} catch (IOException e) {
Utils.error(ReplayModEditor.LOGGER, this, CrashReport.makeCrashReport(e, "Running marker processor"), this::close);
}
ReplayMod.instance.runLater(() -> {
progressPopup.close();
close();
});
}).start();
}
@Override
public void open() {
super.open();
}
@Override
protected GuiEditReplay getThis() {
return this;
}
private class ProgressPopup extends AbstractGuiPopup<ProgressPopup> {
private final GuiProgressBar progressBar = new GuiProgressBar(popup).setSize(300, 20);
ProgressPopup(GuiContainer container) {
super(container);
open();
}
@Override
public void close() {
super.close();
}
@Override
protected ProgressPopup getThis() {
return this;
}
}
private class EditTimeline extends GuiMarkerTimeline {
EditTimeline(Set<Marker> markers, Consumer<Set<Marker>> saveMarkers) {
super(markers, saveMarkers);
}
@Override
protected void drawMarkers(GuiRenderer renderer, ReadableDimension size) {
drawCutQuads(renderer, size);
super.drawMarkers(renderer, size);
}
@Override
protected void drawMarker(GuiRenderer renderer, ReadableDimension size, Marker marker, int markerX) {
if (MarkerProcessor.MARKER_NAME_SPLIT.equals(marker.getName())) {
int height = size.getHeight() - BORDER_BOTTOM - BORDER_TOP - MARKER_SIZE + 1;
for (int y = 0; y < height; y += 3) {
renderer.drawRect(markerX, BORDER_TOP + y, 1, 2, Color.WHITE);
}
}
super.drawMarker(renderer, size, marker, markerX);
}
private void drawCutQuads(GuiRenderer renderer, ReadableDimension size) {
boolean inCut = false;
int startTime = 0;
for (Marker marker : markers.stream().sorted(Comparator.comparing(Marker::getTime)).collect(Collectors.toList())) {
if (MarkerProcessor.MARKER_NAME_START_CUT.equals(marker.getName()) && !inCut) {
inCut = true;
startTime = marker.getTime();
} else if (MarkerProcessor.MARKER_NAME_END_CUT.equals(marker.getName()) && inCut) {
drawCutQuad(renderer, size, startTime, marker.getTime());
inCut = false;
}
}
if (inCut) {
drawCutQuad(renderer, size, startTime, getLength());
}
}
private void drawCutQuad(GuiRenderer renderer, ReadableDimension size, int startFrameTime, int endFrameTime) {
int visibleWidth = size.getWidth() - BORDER_LEFT - BORDER_RIGHT;
int startTime = getOffset();
int visibleTime = (int) (getZoom() * getLength());
int endTime = getOffset() + visibleTime;
if (startFrameTime >= endTime || endFrameTime <= startTime) {
return; // Segment out of display range
}
double relativeStart = startFrameTime - startTime;
double relativeEnd = endFrameTime - startTime;
int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + MARKER_SIZE / 2 + 1);
int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - MARKER_SIZE / 2);
if (startX < endX) {
renderer.drawRect(startX + 1, size.getHeight() - BORDER_BOTTOM - MARKER_SIZE,
endX - startX - 2, MARKER_SIZE - 2, Color.RED);
}
}
}
}

View File

@@ -34,10 +34,10 @@ import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
import java.io.File;

View File

@@ -1,7 +1,10 @@
package com.replaymod.editor.gui;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.gson.JsonObject;
import com.replaymod.core.utils.Utils;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.filter.ChangeTimestampFilter;
import com.replaymod.replaystudio.filter.RemoveFilter;
@@ -12,21 +15,20 @@ import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.stream.PacketStream;
import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiNumberField;
import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu;
import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import net.minecraft.client.resources.I18n;
import net.minecraft.crash.CrashReport;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.util.Dimension;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -50,9 +52,10 @@ public class GuiTrimPanel extends GuiPanel {
private final GuiReplayEditor gui;
public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this)
.setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay())
.setToString(f -> f == NO_REPLAY ? "" : Utils.fileNameToReplayName(f.getName()));
private final GuiButton inputReplayButton = new GuiButton(this)
.setMinSize(new Dimension(200, 20))
.setMaxSize(new Dimension(350, 20));
private File inputReplay = NO_REPLAY;
public final GuiNumberField startHour = newGuiNumberField();
public final GuiNumberField startMin = newGuiNumberField();
@@ -104,24 +107,31 @@ public class GuiTrimPanel extends GuiPanel {
endMarker.setToString(toString).setMinSize(new Dimension(100, 20)).onSelection(i ->
onSelectedMarkerChanged(endMarker, endHour, endMin, endSec, endMilli));
File[] files = null;
try {
File folder = gui.getMod().getReplayFolder();
files = folder.listFiles((FileFilter) new SuffixFileFilter(".mcpr", IOCase.INSENSITIVE));
} catch (IOException e) {
Utils.error(LOGGER, gui, CrashReport.makeCrashReport(e, "Listing available replays"), gui.backButton::onClick);
}
if (files == null) {
LOGGER.warn("Replay file list is null, has the replay folder been deleted?");
files = new File[0];
} else {
LOGGER.debug("Found {} replays in the replay folder", files.length);
}
if (files.length == 0) {
inputReplays.setValues(NO_REPLAY);
} else {
inputReplays.setValues(files);
}
inputReplayButton.onClick(() -> {
File folder;
try {
folder = gui.getMod().getReplayFolder();
} catch (IOException e) {
Utils.error(LOGGER, gui, CrashReport.makeCrashReport(e, "Getting replay folder"), gui.backButton::onClick);
return;
}
GuiReplayViewer.GuiSelectReplayPopup popup = GuiReplayViewer.GuiSelectReplayPopup.openGui(gui, folder);
Futures.addCallback(popup.getFuture(), new FutureCallback<File>() {
@Override
public void onSuccess(@Nullable File result) {
if (result != null) {
inputReplay = result;
updateSelectedReplay();
updateReadyState();
}
}
@Override
public void onFailure(Throwable t) {
Utils.error(LOGGER, gui, CrashReport.makeCrashReport(t, "Selecting replay"), gui.backButton::onClick);
}
});
});
updateSelectedReplay();
updateReadyState();
@@ -135,7 +145,7 @@ public class GuiTrimPanel extends GuiPanel {
JsonObject config = new JsonObject();
config.addProperty("offset", -start);
// Pass filters to save dialog
gui.save(inputReplays.getSelectedValue(),
gui.save(inputReplay,
Pair.of(new PacketStream.FilterInfo(new SquashFilter(), -1, start), new JsonObject()),
Pair.of(new PacketStream.FilterInfo(ctf, start, end), config),
Pair.of(new PacketStream.FilterInfo(new RemoveFilter(), end, -1), new JsonObject())
@@ -161,7 +171,11 @@ public class GuiTrimPanel extends GuiPanel {
}
private void updateSelectedReplay() {
File file = inputReplays.getSelectedValue();
File file = inputReplay;
// Update input button label
inputReplayButton.setLabel(file == NO_REPLAY ? I18n.format("gui.none") : Utils.fileNameToReplayName(file.getName()));
// Load markers and meta data from replay file
ReplayMetaData metaData;
Set<Marker> markers;
@@ -196,6 +210,6 @@ public class GuiTrimPanel extends GuiPanel {
}
private void updateReadyState() {
gui.saveButton.setEnabled(inputReplays.getSelectedValue() != NO_REPLAY);
gui.saveButton.setEnabled(inputReplay != NO_REPLAY);
}
}

View File

@@ -0,0 +1,180 @@
package com.replaymod.editor.gui;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.Studio;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.filter.SquashFilter;
import com.replaymod.replaystudio.filter.StreamFilter;
import com.replaymod.replaystudio.io.ReplayInputStream;
import com.replaymod.replaystudio.io.ReplayOutputStream;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.stream.IteratorStream;
import com.replaymod.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.util.Utils;
import org.apache.commons.io.FilenameUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class MarkerProcessor {
/**
* Signals the start of a section which is to be cut from the replay.
*/
public static final String MARKER_NAME_START_CUT = "_RM_START_CUT";
/**
* Signals the end of a section which is to be cut from the replay.
*/
public static final String MARKER_NAME_END_CUT = "_RM_END_CUT";
/**
* Signals the point at which a replay is to be split into multiple replays.
* If it is found in a section which is to be cut, that section will be cut from both replays.
*/
public static final String MARKER_NAME_SPLIT = "_RM_SPLIT";
private static boolean hasWork(Path path) throws IOException {
try (ZipReplayFile inputReplayFile = new ZipReplayFile(new ReplayStudio(), path.toFile())) {
return inputReplayFile.getMarkers().or(HashSet::new).stream().anyMatch(m -> m.getName().startsWith("_RM_"));
}
}
public static void apply(Path path, Consumer<Float> progress) throws IOException {
if (!hasWork(path)) {
return;
}
String replayName = FilenameUtils.getBaseName(path.getFileName().toString());
int splitCounter = 0;
Studio studio = new ReplayStudio();
SquashFilter squashFilter = new SquashFilter();
squashFilter.init(studio, null);
Path inputPath = path.resolveSibling("raw").resolve(path.getFileName());
Files.createDirectories(inputPath.getParent());
Files.move(path, inputPath);
try (ZipReplayFile inputReplayFile = new ZipReplayFile(studio, inputPath.toFile())) {
List<Marker> markers = inputReplayFile.getMarkers().or(HashSet::new)
.stream().sorted(Comparator.comparing(Marker::getTime)).collect(Collectors.toList());
Iterator<Marker> markerIterator = markers.iterator();
boolean anySplit = markers.stream().anyMatch(m -> m.getName().equals(MARKER_NAME_SPLIT));
int inputDuration = inputReplayFile.getMetaData().getDuration();
ReplayInputStream replayInputStream = inputReplayFile.getPacketData(studio, true);
int timeOffset = 0;
SquashFilter cutFilter = null;
int startCutOffset = 0;
PacketData nextPacket = replayInputStream.readPacket();
Marker nextMarker = markerIterator.next();
while (nextPacket != null) {
try (ZipReplayFile outputReplayFile = new ZipReplayFile(studio, null,
(anySplit ? path.resolveSibling(replayName + "_" + splitCounter + ".mcpr") : path).toFile())) {
long duration = 0;
Set<Marker> outputMarkers = new HashSet<>();
ReplayMetaData metaData = inputReplayFile.getMetaData();
metaData.setDate(metaData.getDate() + timeOffset);
try (ReplayOutputStream replayOutputStream = outputReplayFile.writePacketData(true)) {
if (cutFilter != null) {
cutFilter = squashFilter;
} else if (splitCounter > 0) {
List<PacketData> packets = new ArrayList<>();
squashFilter.onEnd(
new IteratorStream(packets.listIterator(), (StreamFilter) null),
timeOffset
);
for (PacketData packet : packets) {
replayOutputStream.write(0, packet.getPacket());
}
}
while (nextPacket != null) {
if (nextMarker != null && nextPacket.getTime() > nextMarker.getTime()) {
if (MARKER_NAME_START_CUT.equals(nextMarker.getName())) {
startCutOffset = nextMarker.getTime();
cutFilter = new SquashFilter();
} else if (MARKER_NAME_END_CUT.equals(nextMarker.getName())) {
timeOffset += nextMarker.getTime() - startCutOffset;
if (cutFilter != null) {
List<PacketData> packets = new ArrayList<>();
cutFilter.onEnd(
new IteratorStream(packets.listIterator(), (StreamFilter) null),
nextMarker.getTime()
);
for (PacketData packet : packets) {
replayOutputStream.write(nextMarker.getTime() - timeOffset, packet.getPacket());
}
cutFilter = null;
}
} else if (MARKER_NAME_SPLIT.equals(nextMarker.getName())) {
splitCounter++;
timeOffset = nextMarker.getTime();
startCutOffset = timeOffset;
nextMarker = markerIterator.hasNext() ? markerIterator.next() : null;
break;
} else {
nextMarker.setTime(nextMarker.getTime() - timeOffset);
outputMarkers.add(nextMarker);
}
nextMarker = markerIterator.hasNext() ? markerIterator.next() : null;
continue;
}
squashFilter.onPacket(null, nextPacket);
if (cutFilter != null) {
if (cutFilter != squashFilter) {
cutFilter.onPacket(null, nextPacket);
}
} else {
replayOutputStream.write(nextPacket.getTime() - timeOffset, nextPacket.getPacket());
duration = nextPacket.getTime() - timeOffset;
}
nextPacket = replayInputStream.readPacket();
if (nextPacket != null) {
progress.accept((float) nextPacket.getTime() / (float) inputDuration);
} else {
progress.accept(1f);
}
}
}
metaData.setDuration((int) duration);
outputReplayFile.writeMetaData(metaData);
outputReplayFile.writeMarkers(outputMarkers);
outputReplayFile.writeModInfo(inputReplayFile.getModInfo());
// We could filter these but ReplayStudio doesn't yet provide a nice method for determining
// which ones are used.
Map<Integer, String> resourcePackIndex = inputReplayFile.getResourcePackIndex();
if (resourcePackIndex != null) {
outputReplayFile.writeResourcePackIndex(resourcePackIndex);
for (String hash : resourcePackIndex.values()) {
try (InputStream in = inputReplayFile.getResourcePack(hash).get();
OutputStream out = outputReplayFile.writeResourcePack(hash)) {
Utils.copy(in, out);
}
}
}
outputReplayFile.save();
}
}
}
}
}

View File

@@ -1,55 +1,55 @@
package com.replaymod.editor.handler;
import com.replaymod.core.utils.Utils;
import com.replaymod.editor.ReplayModEditor;
import com.replaymod.editor.gui.GuiReplayEditor;
import com.replaymod.editor.gui.GuiEditReplay;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.resources.I18n;
import de.johni0702.minecraft.gui.element.GuiButton;
import net.minecraft.crash.CrashReport;
import net.minecraftforge.client.event.GuiScreenEvent;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
import java.io.IOException;
import static com.replaymod.core.versions.MCVer.*;
public class GuiHandler {
private static final int BUTTON_REPLAY_EDITOR = 17890237;
private final ReplayModEditor mod;
public GuiHandler(ReplayModEditor mod) {
this.mod = mod;
}
public void register() {
FML_BUS.register(this);
FORGE_BUS.register(this);
}
@SubscribeEvent
public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) {
if (!(getGui(event) instanceof GuiMainMenu)) {
public void injectIntoReplayViewer(GuiScreenEvent.InitGuiEvent.Post event) {
AbstractGuiScreen guiScreen = GuiScreen.from(getGui(event));
if (!(guiScreen instanceof GuiReplayViewer)) {
return;
}
GuiButton button = new GuiButton(BUTTON_REPLAY_EDITOR, getGui(event).width / 2 + 2,
getGui(event).height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayeditor"));
button.width = button.width / 2 - 2;
getButtonList(event).add(button);
}
@SubscribeEvent
public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) {
if(!getButton(event).enabled) return;
if (getGui(event) instanceof GuiMainMenu) {
if (getButton(event).id == BUTTON_REPLAY_EDITOR) {
new GuiReplayEditor(GuiScreen.wrap(getGui(event)), mod.getCore()).display();
final GuiReplayViewer replayViewer = (GuiReplayViewer) guiScreen;
// Inject Edit button
replayViewer.replaySpecificButtons.add(new GuiButton(replayViewer.editorButton).onClick(() -> {
try {
new GuiEditReplay(replayViewer, replayViewer.list.getSelected().file.toPath()) {
@Override
protected void close() {
super.close();
replayViewer.list.load();
}
}.open();
} catch (IOException e) {
Utils.error(ReplayModEditor.LOGGER, replayViewer, CrashReport.makeCrashReport(e, "Opening replay editor"), () -> {});
}
}
}).setSize(73, 20).setI18nLabel("replaymod.gui.edit").setDisabled());
}
}

View File

@@ -8,11 +8,21 @@ import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import de.johni0702.minecraft.gui.element.GuiImage;
import de.johni0702.minecraft.gui.element.IGuiImage;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import net.minecraft.client.settings.GameSettings;
import org.lwjgl.input.Keyboard;
//#if MC>=11300
import com.replaymod.core.versions.MCVer.Keyboard;
import net.minecraft.client.GameSettings;
//#else
//$$ import net.minecraft.client.settings.GameSettings;
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import net.minecraftforge.fml.common.gameevent.TickEvent;
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
@@ -28,7 +38,11 @@ public class FullBrightness implements Extra {
private GameSettings gameSettings;
private boolean active;
private float originalGamma;
//#if MC>=11300
private double originalGamma;
//#else
//$$ private float originalGamma;
//#endif
@Override
public void register(final ReplayMod mod) throws Exception {
@@ -39,7 +53,11 @@ public class FullBrightness implements Extra {
@Override
public void run() {
active = !active;
mod.getMinecraft().entityRenderer.lightmapUpdateNeeded = true;
//#if MC>=11300
mod.getMinecraft().entityRenderer.tick(); // need to tick once to update lightmap when replay is paused
//#else
//$$ mod.getMinecraft().entityRenderer.lightmapUpdateNeeded = true;
//#endif
ReplayHandler replayHandler = module.getReplayHandler();
if (replayHandler != null) {
updateIndicator(replayHandler.getOverlay());

View File

@@ -16,13 +16,21 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.LayoutData;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.client.resources.I18n;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
//#if MC>=11300
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
@@ -89,7 +97,11 @@ public class HotkeyButtons implements Extra {
// There doesn't seem to be an KeyBindingUpdate event, so we'll just update it every time
String keyName = "???";
try {
keyName = Keyboard.getKeyName(keyBinding.getKeyCode());
//#if MC>=11300
keyName = keyBinding.func_197978_k();
//#else
//$$ keyName = Keyboard.getKeyName(keyBinding.getKeyCode());
//#endif
} catch (ArrayIndexOutOfBoundsException e) {
// Apparently windows likes to press strange keys, see https://www.replaymod.com/forum/thread/55
}

View File

@@ -1,134 +0,0 @@
package com.replaymod.extras;
import com.google.common.collect.ImmutableSet;
import com.replaymod.compat.optifine.OptifineReflection;
import com.replaymod.core.ReplayMod;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResourcePack;
import net.minecraft.client.resources.data.IMetadataSection;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.IOUtils;
//#if MC>=10904
import net.minecraft.client.resources.data.MetadataSerializer;
//#else
//$$ import net.minecraft.client.resources.data.IMetadataSerializer;
//#endif
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class LocalizationExtra implements Extra {
private static final String ZIP_FILE_URL = "https://github.com/ReplayMod/Translations/archive/master.zip";
private static final String LANG_PREFIX = "Translations-master/";
@Override
public void register(ReplayMod mod) throws Exception {
final Minecraft mc = mod.getMinecraft();
if (Boolean.parseBoolean(System.getProperty("replaymod.offline", "false"))) {
return;
}
Thread localizedResourcePackLoader = new Thread(() -> {
try {
// Download zip of lang files
LOGGER.debug("Downloading languages from {}", ZIP_FILE_URL);
Map<String, byte[]> languages = new HashMap<>();
try (InputStream urlIn = new URL(ZIP_FILE_URL).openStream();
ZipInputStream in = new ZipInputStream(urlIn)) {
ZipEntry entry;
while ((entry = in.getNextEntry()) != null) {
String name = entry.getName();
if (!name.startsWith(LANG_PREFIX) || !name.endsWith(".lang")) {
continue;
}
name = name.substring(LANG_PREFIX.length());
languages.put(name, IOUtils.toByteArray(in));
LOGGER.debug("Added language file {}", name);
}
}
LOGGER.debug("Downloaded {} languages", languages.size());
// Add lang files as resource pack
mc.addScheduledTask(() -> {
@SuppressWarnings("unchecked")
List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks;
defaultResourcePacks.add(new LocalizedResourcePack(languages));
mc.getLanguageManager().onResourceManagerReload(mc.getResourceManager());
// Optifine does its own, additional language data loading
OptifineReflection.reloadLang();
LOGGER.debug("Added language files to resource packs and reloaded LanguageManager");
});
} catch (Throwable t) {
LOGGER.error("Loading localized resource pack:", t);
}
}, "localizedResourcePackLoader");
localizedResourcePackLoader.setDaemon(true);
localizedResourcePackLoader.start();
}
public static class LocalizedResourcePack implements IResourcePack {
private final Pattern LANG_PATTERN = Pattern.compile("^lang/([.+].lang)$");
private final Map<String, byte[]> languages;
public LocalizedResourcePack(Map<String, byte[]> languages) {
this.languages = languages;
}
@Override
public InputStream getInputStream(ResourceLocation loc) {
if (!"replaymod".equals(loc.getResourceDomain())) return null;
Matcher matcher = LANG_PATTERN.matcher(loc.getResourcePath());
if (matcher.matches()) {
byte[] bytes = languages.get(matcher.group());
if (bytes != null) {
return new ByteArrayInputStream(bytes);
}
}
return null;
}
@Override
public boolean resourceExists(ResourceLocation loc) {
// Assumes that getInputStream returns a ByteArrayInputStream that doesn't need to be closed
return getInputStream(loc) != null;
}
@Override
public Set<String> getResourceDomains() {
return ImmutableSet.of("replaymod");
}
//#if MC>=10904
@Override
public <T extends IMetadataSection> T getPackMetadata(MetadataSerializer metadataSerializer, String metadataSectionName) throws IOException {
//#else
//$$ @Override
//$$ public IMetadataSection getPackMetadata(IMetadataSerializer metadataSerializer, String metadataSectionName) throws IOException {
//#endif
return null;
}
@Override
public BufferedImage getPackImage() throws IOException {
return null;
}
@Override
public String getPackName() {
return "ReplayModLocalizationResourcePack";
}
}
}

View File

@@ -1,7 +1,7 @@
package com.replaymod.extras;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.Setting;
import com.replaymod.core.versions.MCVer;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
@@ -16,11 +16,15 @@ import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import org.apache.commons.io.FileUtils;
//#if MC>=11300
import net.minecraftforge.fml.ModList;
//#else
//#if MC>=10800
import net.minecraftforge.fml.common.Loader;
//$$ import net.minecraftforge.fml.common.Loader;
//#else
//$$ import cpw.mods.fml.common.Loader;
//#endif
//#endif
import javax.net.ssl.HttpsURLConnection;
import java.io.File;
@@ -34,17 +38,20 @@ import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class OpenEyeExtra implements Extra {
private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + Loader.MC_VERSION;
private static final Setting<Boolean> ASK_FOR_OPEN_EYE = new Setting<>("advanced", "askForOpenEye", null, true);
private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + ReplayMod.getMinecraftVersion();
private ReplayMod mod;
@Override
public void register(ReplayMod mod) throws Exception {
this.mod = mod;
mod.getSettingsRegistry().register(ASK_FOR_OPEN_EYE);
if (!Loader.isModLoaded("OpenEye") && mod.getSettingsRegistry().get(ASK_FOR_OPEN_EYE)) {
//#if MC>=11300
boolean isOpenEyeLoaded = ModList.get().isLoaded("openeye");
//#else
//$$ boolean isOpenEyeLoaded = Loader.isModLoaded("OpenEye");
//#endif
if (!isOpenEyeLoaded && mod.getSettingsRegistry().get(Setting.ASK_FOR_OPEN_EYE)) {
new Thread(() -> {
try {
LOGGER.trace("Checking for OpenEye availability");
@@ -93,7 +100,7 @@ public class OpenEyeExtra implements Extra {
GuiPopup popup = new GuiPopup(OfferGui.this);
new Thread(() -> {
try {
File targetFile = new File(mod.getMinecraft().mcDataDir, "mods/" + Loader.MC_VERSION + "/OpenEye.jar");
File targetFile = new File(MCVer.mcDataDir(mod.getMinecraft()), "mods/" + ReplayMod.getMinecraftVersion() + "/OpenEye.jar");
FileUtils.forceMkdir(targetFile.getParentFile());
HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection();
@@ -112,7 +119,7 @@ public class OpenEyeExtra implements Extra {
}).start();
});
noButton.onClick(() -> {
mod.getSettingsRegistry().set(ASK_FOR_OPEN_EYE, false);
mod.getSettingsRegistry().set(Setting.ASK_FOR_OPEN_EYE, false);
mod.getSettingsRegistry().save();
parent.display();
});

View File

@@ -1,40 +1,22 @@
package com.replaymod.extras;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod;
import com.replaymod.extras.advancedscreenshots.AdvancedScreenshots;
import com.replaymod.extras.playeroverview.PlayerOverview;
import com.replaymod.extras.urischeme.UriSchemeExtra;
import com.replaymod.extras.youtube.YoutubeUpload;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if MC>=10800
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
//#else
//$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//#endif
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Mod(modid = ReplayModExtras.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModExtras {
public static final String MOD_ID = "replaymod-extras";
@Mod.Instance(MOD_ID)
public class ReplayModExtras implements Module {
{ instance = this; }
public static ReplayModExtras instance;
private static final List<Class<? extends Extra>> builtin = Arrays.asList(
@@ -44,22 +26,19 @@ public class ReplayModExtras {
YoutubeUpload.class,
FullBrightness.class,
HotkeyButtons.class,
LocalizationExtra.class,
OpenEyeExtra.class
);
private final Map<Class<? extends Extra>, Extra> instances = new HashMap<>();
public static Logger LOGGER;
public static Logger LOGGER = LogManager.getLogger();
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
LOGGER = event.getModLog();
ReplayMod.instance.getSettingsRegistry().register(Setting.class);
public ReplayModExtras(ReplayMod core) {
core.getSettingsRegistry().register(Setting.class);
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
@Override
public void initClient() {
for (Class<? extends Extra> cls : builtin) {
try {
Extra extra = cls.newInstance();

View File

@@ -3,6 +3,8 @@ package com.replaymod.extras;
import com.replaymod.core.SettingsRegistry;
public final class Setting<T> {
public static final SettingsRegistry.SettingKey<Boolean> ASK_FOR_OPEN_EYE =
new SettingsRegistry.SettingKeys<>("advanced", "askForOpenEye", null, true);
public static final SettingsRegistry.SettingKey<Boolean> SKIP_POST_SCREENSHOT_GUI =
new SettingsRegistry.SettingKeys<>("advanced", "skipPostScreenshotGui", null, false);
}

View File

@@ -2,14 +2,22 @@ package com.replaymod.extras.advancedscreenshots;
import com.replaymod.core.ReplayMod;
import com.replaymod.extras.Extra;
import com.replaymod.replay.events.ReplayDispatchKeypressesEvent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiControls;
import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.input.Keyboard;
//#if MC<11300
//$$ import com.replaymod.core.versions.MCVer;
//$$ import com.replaymod.replay.events.ReplayDispatchKeypressesEvent;
//$$ import net.minecraft.client.Minecraft;
//$$ import net.minecraft.client.gui.GuiControls;
//$$ import net.minecraftforge.client.event.GuiScreenEvent;
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
@@ -18,29 +26,36 @@ public class AdvancedScreenshots implements Extra {
private ReplayMod mod;
private final Minecraft mc = Minecraft.getMinecraft();
@Override
public void register(ReplayMod mod) throws Exception {
public void register(ReplayMod mod) {
this.mod = mod;
MinecraftForge.EVENT_BUS.register(this);
}
@SubscribeEvent
public void onDispatchKeypresses(ReplayDispatchKeypressesEvent.Pre event) {
int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() : Keyboard.getEventKey();
// all the conditions required to trigger a screenshot condensed in a single if statement
if (keyCode != 0 && !Keyboard.isRepeatEvent()
&& (!(mc.currentScreen instanceof GuiControls) || ((GuiControls) mc.currentScreen).time <= mc.getSystemTime() - 20L)
&& Keyboard.getEventKeyState()
&& keyCode == mc.gameSettings.keyBindScreenshot.getKeyCode()) {
ReplayMod.instance.runLater(() -> {
new GuiCreateScreenshot(mod).display();
});
event.setCanceled(true);
//#if MC>=11300
private static AdvancedScreenshots instance; { instance = this; }
public static void take() {
if (instance != null) {
instance.takeScreenshot();
}
}
//#else
//$$ @SubscribeEvent
//$$ public void onDispatchKeypresses(ReplayDispatchKeypressesEvent.Pre event) {
//$$ Minecraft mc = MCVer.getMinecraft();
//$$ if (mc.currentScreen instanceof GuiControls) return;
//$$ if (!Keyboard.getEventKeyState()) return;
//$$ if (Keyboard.isRepeatEvent()) return;
//$$ int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() : Keyboard.getEventKey();
//$$ if (keyCode == 0 || keyCode != mc.gameSettings.keyBindScreenshot.getKeyCode()) return;
//$$
//$$ takeScreenshot();
//$$
//$$ event.setCanceled(true);
//$$ }
//#endif
private void takeScreenshot() {
ReplayMod.instance.runLater(() -> new GuiCreateScreenshot(mod).display());
}
}

View File

@@ -1,6 +1,7 @@
package com.replaymod.extras.advancedscreenshots;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.gui.GuiRenderSettings;
import com.replaymod.replay.ReplayModReplay;
@@ -12,10 +13,9 @@ import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ScreenShotHelper;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import static com.replaymod.core.utils.Utils.error;
@@ -82,13 +82,12 @@ public class GuiCreateScreenshot extends GuiRenderSettings implements Loadable {
@Override
protected File generateOutputFile(RenderSettings.EncodingPreset encodingPreset) {
File screenshotFolder = new File(getMinecraft().mcDataDir, "screenshots");
File screenshotFolder = new File(MCVer.mcDataDir(getMinecraft()), "screenshots");
return ScreenShotHelper.getTimestampedPNGFileForDirectory(screenshotFolder);
}
@Override
protected Property getConfigProperty(Configuration configuration) {
return configuration.get("screenshotsettings", "settings", "{}",
"Last state of the screenshot settings GUI. Internal use only.");
protected Path getSettingsPath() {
return MCVer.mcDataDir(getMinecraft()).toPath().resolve("config/replaymod-screenshotsettings.json");
}
}

View File

@@ -12,7 +12,7 @@ import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import org.lwjgl.util.ReadableColor;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import java.awt.*;
import java.io.IOException;

View File

@@ -1,14 +1,15 @@
package com.replaymod.extras.advancedscreenshots;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.blend.BlendState;
import com.replaymod.render.capturer.RenderInfo;
import com.replaymod.render.rendering.Pipelines;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import lombok.RequiredArgsConstructor;
import net.minecraft.client.Minecraft;
import net.minecraft.crash.CrashReport;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
//#if MC>=10800
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
@@ -17,7 +18,7 @@ import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
@RequiredArgsConstructor
public class ScreenshotRenderer implements RenderInfo {
private final Minecraft mc = Minecraft.getMinecraft();
private final Minecraft mc = MCVer.getMinecraft();
private final RenderSettings settings;
@@ -25,8 +26,13 @@ public class ScreenshotRenderer implements RenderInfo {
public boolean renderScreenshot() throws Throwable {
try {
int displayWidthBefore = mc.displayWidth;
int displayHeightBefore = mc.displayHeight;
//#if MC>=11300
int displayWidthBefore = mc.mainWindow.framebufferWidth;
int displayHeightBefore = mc.mainWindow.framebufferHeight;
//#else
//$$ int displayWidthBefore = mc.displayWidth;
//$$ int displayHeightBefore = mc.displayHeight;
//#endif
boolean hideGUIBefore = mc.gameSettings.hideGUI;
mc.gameSettings.hideGUI = true;
@@ -48,12 +54,18 @@ public class ScreenshotRenderer implements RenderInfo {
//#endif
mc.gameSettings.hideGUI = hideGUIBefore;
mc.resize(displayWidthBefore, displayHeightBefore);
//#if MC>=11300
mc.mainWindow.framebufferWidth = displayWidthBefore;
mc.mainWindow.framebufferHeight = displayHeightBefore;
mc.getFramebuffer().createBindFramebuffer(displayWidthBefore, displayHeightBefore);
//#else
//$$ mc.resize(displayWidthBefore, displayHeightBefore);
//#endif
return true;
} catch (OutOfMemoryError e) {
e.printStackTrace();
CrashReport report = CrashReport.makeCrashReport(e, "Creating Equirectangular Screenshot");
Minecraft.getMinecraft().crashed(report);
MCVer.getMinecraft().crashed(report);
}
return false;
}

View File

@@ -2,13 +2,13 @@ package com.replaymod.extras.advancedscreenshots;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.core.versions.MCVer;
import com.replaymod.extras.ReplayModExtras;
import com.replaymod.render.frame.RGBFrame;
import com.replaymod.render.rendering.FrameConsumer;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.Minecraft;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport;
import org.lwjgl.util.ReadableDimension;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@@ -48,7 +48,7 @@ public class ScreenshotWriter implements FrameConsumer<RGBFrame> {
} catch (OutOfMemoryError e) {
e.printStackTrace();
CrashReport report = CrashReport.makeCrashReport(e, "Exporting frame");
Minecraft.getMinecraft().crashed(report);
MCVer.getMinecraft().crashed(report);
} catch (Throwable t) {
CrashReport report = CrashReport.makeCrashReport(t, "Exporting frame");

View File

@@ -11,11 +11,19 @@ import com.replaymod.replay.events.ReplayOpenEvent;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.client.event.RenderHandEvent;
import org.lwjgl.input.Keyboard;
//#if MC>=11300
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800
import com.google.common.base.Predicate;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.EventPriority;
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;

View File

@@ -18,15 +18,13 @@ import de.johni0702.minecraft.gui.function.Closeable;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.audio.PositionedSoundRecord;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
//#if MC>=10904
import net.minecraft.init.MobEffects;
import net.minecraft.init.SoundEvents;
//#else
//$$ import net.minecraft.potion.Potion;
//#endif
@@ -55,30 +53,12 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public final GuiCheckbox checkAll = new GuiCheckbox(contentPanel){
@Override
public void onClick() {
//#if MC>=10904
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
//#else
//#if MC>=10800
//$$ getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(BUTTON_SOUND, 1.0F));
//#else
//$$ getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.createPositionedSoundRecord(BUTTON_SOUND, 1.0F));
//#endif
//#endif
playersScrollable.forEach(IGuiCheckbox.class).setChecked(true);
}
}.setLabel("").setChecked(true).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.showall"));
public final GuiCheckbox uncheckAll = new GuiCheckbox(contentPanel){
@Override
public void onClick() {
//#if MC>=10904
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
//#else
//#if MC>=10800
//$$ getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(BUTTON_SOUND, 1.0F));
//#else
//$$ getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.createPositionedSoundRecord(BUTTON_SOUND, 1.0F));
//#endif
//#endif
playersScrollable.forEach(IGuiCheckbox.class).setChecked(false);
}
}.setLabel("").setChecked(false).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.hideall"));
@@ -135,11 +115,15 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
}
}.setSize(16, 16),
new GuiLabel().setText(
//#if MC>=11300
p.getName().getFormattedText()
//#else
//#if MC>=10800
p.getName()
//$$ p.getName()
//#else
//$$ p.getDisplayName()
//#endif
//#endif
).setColor(isSpectator(p) ? Colors.DKGREY : Colors.WHITE)
).onClick(new Runnable() {
@Override
@@ -196,11 +180,15 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public int compare(EntityPlayer o1, EntityPlayer o2) {
if (isSpectator(o1) && !isSpectator(o2)) return 1;
if (isSpectator(o2) && !isSpectator(o1)) return -1;
//#if MC>=11300
return o1.getName().getFormattedText().compareToIgnoreCase(o2.getName().getFormattedText());
//#else
//#if MC>=10800
return o1.getName().compareToIgnoreCase(o2.getName());
//$$ return o1.getName().compareToIgnoreCase(o2.getName());
//#else
//$$ return o1.getDisplayName().compareToIgnoreCase(o2.getDisplayName());
//#endif
//#endif
}
}
}

View File

@@ -1,6 +1,5 @@
package com.replaymod.extras.urischeme;
import com.replaymod.core.ReplayMod;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.StringBuilderWriter;
@@ -17,7 +16,7 @@ public class LinuxUriScheme extends UriScheme {
public void install() throws URISyntaxException, IOException {
File file = new File("replaymod.desktop");
File iconFile = new File("replaymod-icon.jpg");
String path = ReplayMod.getContainer().getSource().getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"");
String path = findJarFile().getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"");
String content =
"[Desktop Entry]\n" +
"Name=ReplayMod\n" +

View File

@@ -6,6 +6,9 @@ import javax.swing.*;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.util.Arrays;
public abstract class UriScheme {
@@ -82,4 +85,19 @@ public abstract class UriScheme {
}
public abstract void install() throws Exception;
File findJarFile() throws IOException {
CodeSource codeSource = UriScheme.class.getProtectionDomain().getCodeSource();
if (codeSource != null) {
URL location = codeSource.getLocation();
if (location != null) {
try {
return new File(location.toURI());
} catch (URISyntaxException e) {
throw new IOException(e);
}
}
}
throw new IOException("No jar file found. Is this a development environment?");
}
}

View File

@@ -1,6 +1,5 @@
package com.replaymod.extras.urischeme;
import com.replaymod.core.ReplayMod;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.StringBuilderWriter;
@@ -9,7 +8,7 @@ import java.io.IOException;
public class WindowsUriScheme extends UriScheme {
@Override
public void install() throws IOException, InterruptedException {
String path = ReplayMod.getContainer().getSource().getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"");
String path = findJarFile().getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"");
regAdd("\\replaymod /f /ve /d \"URL:replaymod Protocol\"");
regAdd("\\replaymod /f /v \"URL Protocol\" /d \"\"");
regAdd("\\replaymod\\shell\\open\\command /f /ve /d \"java -cp \\\"" + path + "\\\" " + UriScheme.class.getName() + " \\\"%1\\\"\"");

View File

@@ -18,12 +18,17 @@ import de.johni0702.minecraft.gui.element.advanced.GuiTextArea;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiFileChooserPopup;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import joptsimple.internal.Strings;
import lombok.RequiredArgsConstructor;
import net.minecraft.client.resources.I18n;
import org.apache.commons.io.IOUtils;
import org.lwjgl.Sys;
import org.lwjgl.util.ReadableDimension;
//#if MC>=11300
import net.minecraft.util.Util;
//#else
//$$ import org.lwjgl.Sys;
//#endif
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
@@ -213,7 +218,11 @@ public class GuiYoutubeUpload extends GuiScreen {
try {
Desktop.getDesktop().browse(new URL(url).toURI());
} catch(Throwable throwable) {
Sys.openURL(url);
//#if MC>=11300
Util.getOSType().openURI(url);
//#else
//$$ Sys.openURL(url);
//#endif
}
upload = null;
progressBar.setLabel(I18n.format("replaymod.gui.ytuploadprogress.done", url));

View File

@@ -9,7 +9,11 @@ import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif

View File

@@ -21,8 +21,10 @@ import com.google.api.services.youtube.model.VideoSnippet;
import com.google.api.services.youtube.model.VideoStatus;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.metadata.MetadataInjector;
import lombok.Getter;
@@ -41,8 +43,7 @@ import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class YoutubeUploader {
private static final String CLIENT_ID = "743126594724-mfe7pj1k7e47uu5pk4503c8st9vj9ibu.apps.googleusercontent.com";
private static final String CLIENT_SECRET = "gMwcy3mRYCRamCIjJIYP7rqc";
private static final String FFMPEG_ODS =
"-i %s -vf scale=iw:iw*9/16,setdar=16:9 -c:v libx264 -preset slow -crf 16 %s";
private static final String FFMPEG_MP4 = "-i %s -c:v libx264 -preset slow -crf 16 %s";
private static final JsonFactory JSON_FACTORY = new GsonFactory();
private final NetHttpTransport httpTransport;
private final DataStoreFactory dataStoreFactory;
@@ -78,7 +79,7 @@ public class YoutubeUploader {
this.videoVisibility = videoVisibility;
this.videoSnippet = videoSnippet;
this.httpTransport = GoogleNetHttpTransport.newTrustedTransport();
this.dataStoreFactory = new FileDataStoreFactory(minecraft.mcDataDir);
this.dataStoreFactory = new FileDataStoreFactory(MCVer.mcDataDir(minecraft));
}
@@ -131,68 +132,73 @@ public class YoutubeUploader {
}
private File preUpload() throws InterruptedException, IOException {
if (settings.getRenderMethod() == RenderSettings.RenderMethod.ODS) {
File tmpFile = new File(videoFile.getParentFile(), System.currentTimeMillis() + ".mp4");
tmpFile.deleteOnExit();
File outputFile = videoFile;
String args = String.format(FFMPEG_ODS, videoFile.getName(), tmpFile.getName());
if (settings.getRenderMethod().isSpherical()) {
// inject spherical metadata for YouTube unless already done
CommandLine commandLine = new CommandLine(settings.getExportCommand());
commandLine.addArguments(args);
LOGGER.info("Re-encoding for ODS with {} {}", settings.getExportCommand(), args);
Process process = new ProcessBuilder(commandLine.toStrings()).directory(videoFile.getParentFile()).start();
boolean isMp4 = Files.getFileExtension(outputFile.getName()).equalsIgnoreCase("mp4");
final AtomicBoolean active = new AtomicBoolean(true);
final InputStream in = process.getErrorStream();
new Thread(() -> {
try {
StringBuilder sb = new StringBuilder();
while (active.get()) {
char c = (char) in.read();
if (c == '\r') {
String str = sb.toString();
LOGGER.debug("[FFmpeg] {}", str);
if (str.startsWith("frame=")) {
str = str.substring(6).trim();
str = str.substring(0, str.indexOf(' '));
double frame = Integer.parseInt(str);
progress = Suppliers.ofInstance(frame / videoFrames);
if (!isMp4) {
// convert non-mp4 videos into mp4 format to be able to inject metadata
outputFile = new File(outputFile.getParentFile(), System.currentTimeMillis() + ".mp4");
outputFile.deleteOnExit();
String args = String.format(FFMPEG_MP4, videoFile.getName(), outputFile.getName());
CommandLine commandLine = new CommandLine(settings.getExportCommandOrDefault());
commandLine.addArguments(args);
LOGGER.info("Re-encoding for metadata injection with {} {}", commandLine.getExecutable(), args);
Process process = new ProcessBuilder(commandLine.toStrings()).directory(outputFile.getParentFile()).start();
final AtomicBoolean active = new AtomicBoolean(true);
final InputStream in = process.getErrorStream();
new Thread(() -> {
try {
StringBuilder sb = new StringBuilder();
while (active.get()) {
char c = (char) in.read();
if (c == '\r') {
String str = sb.toString();
LOGGER.debug("[FFmpeg] {}", str);
if (str.startsWith("frame=")) {
str = str.substring(6).trim();
str = str.substring(0, str.indexOf(' '));
double frame = Integer.parseInt(str);
progress = Suppliers.ofInstance(frame / videoFrames);
}
sb = new StringBuilder();
} else {
sb.append(c);
}
sb = new StringBuilder();
} else {
sb.append(c);
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}).start();
int result;
try {
result = process.waitFor();
} catch (InterruptedException e) {
process.destroy();
throw e;
} finally {
active.set(false);
}
if (result != 0) {
throw new IOException("FFmpeg returned: " + result);
}
}).start();
int result;
try {
result = process.waitFor();
} catch (InterruptedException e) {
process.destroy();
throw e;
} finally {
active.set(false);
}
if (result != 0) {
throw new IOException("FFmpeg returned: " + result);
}
MetadataInjector.injectODSMetadata(tmpFile);
return tmpFile;
} else if (settings.getRenderMethod() == RenderSettings.RenderMethod.EQUIRECTANGULAR) {
// if metadata hasn't been injected before, inject it before the upload
if (!settings.isInject360Metadata()) {
MetadataInjector.inject360Metadata(videoFile);
if (!settings.isInjectSphericalMetadata() || !isMp4) {
MetadataInjector.injectMetadata(settings.getRenderMethod(), outputFile,
settings.getTargetVideoWidth(), settings.getTargetVideoHeight(),
settings.getSphericalFovX(), settings.getSphericalFovY());
}
}
return videoFile;
return outputFile;
}
private Video doUpload(YouTube youTube, File processedFile) throws IOException {

View File

@@ -1,6 +1,6 @@
package com.replaymod.online;
import net.minecraft.client.Minecraft;
import com.replaymod.core.versions.MCVer;
import java.util.Random;
@@ -9,7 +9,7 @@ public class AuthenticationHash {
private static final Random random = new Random();
public AuthenticationHash() {
username = Minecraft.getMinecraft().getSession().getUsername();
username = MCVer.getMinecraft().getSession().getUsername();
currentTime = System.currentTimeMillis();
randomLong = random.nextLong();
hash = getAuthenticationHash();

View File

@@ -1,63 +1,65 @@
package com.replaymod.online;
import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.AuthData;
import com.replaymod.online.api.replay.holders.AuthConfirmation;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
/**
* Auth data stored in a {@link Configuration}.
*/
public class ConfigurationAuthData implements AuthData {
private final Configuration config;
private String userName;
private String authKey;
public ConfigurationAuthData(Configuration config) {
this.config = config;
}
/**
* Loads the data from the configuration and checks for its validity.
* If the data is invalid, it is removed from the config.
* @param apiClient Api client used for validating the auth data
*/
public void load(ApiClient apiClient) {
Property property = config.get("authkey", "authkey", (String) null);
if (property != null) {
String authKey = property.getString();
AuthConfirmation result = apiClient.checkAuthkey(authKey);
if (result != null) {
this.authKey = authKey;
this.userName = result.getUsername();
} else {
setData(null, null);
}
}
}
@Override
public String getUserName() {
return userName;
}
@Override
public String getAuthKey() {
return authKey;
}
@Override
public void setData(String userName, String authKey) {
this.userName = userName;
this.authKey = authKey;
config.removeCategory(config.getCategory("authkey"));
if (authKey != null) {
// Note: .get() actually creates the entry with the default value if it doesn't exist
config.get("authkey", "authkey", authKey);
}
config.save();
}
}
//#if MC<11300
//$$ package com.replaymod.online;
//$$
//$$ import com.replaymod.online.api.ApiClient;
//$$ import com.replaymod.online.api.AuthData;
//$$ import com.replaymod.online.api.replay.holders.AuthConfirmation;
//$$ import net.minecraftforge.common.config.Configuration;
//$$ import net.minecraftforge.common.config.Property;
//$$
//$$ /**
//$$ * Auth data stored in a {@link Configuration}.
//$$ */
//$$ public class ConfigurationAuthData implements AuthData {
//$$
//$$ private final Configuration config;
//$$ private String userName;
//$$ private String authKey;
//$$
//$$ public ConfigurationAuthData(Configuration config) {
//$$ this.config = config;
//$$ }
//$$
//$$ /**
//$$ * Loads the data from the configuration and checks for its validity.
//$$ * If the data is invalid, it is removed from the config.
//$$ * @param apiClient Api client used for validating the auth data
//$$ */
//$$ public void load(ApiClient apiClient) {
//$$ Property property = config.get("authkey", "authkey", (String) null);
//$$ if (property != null) {
//$$ String authKey = property.getString();
//$$ AuthConfirmation result = apiClient.checkAuthkey(authKey);
//$$ if (result != null) {
//$$ this.authKey = authKey;
//$$ this.userName = result.getUsername();
//$$ } else {
//$$ setData(null, null);
//$$ }
//$$ }
//$$ }
//$$
//$$ @Override
//$$ public String getUserName() {
//$$ return userName;
//$$ }
//$$
//$$ @Override
//$$ public String getAuthKey() {
//$$ return authKey;
//$$ }
//$$
//$$ @Override
//$$ public void setData(String userName, String authKey) {
//$$ this.userName = userName;
//$$ this.authKey = authKey;
//$$
//$$ config.removeCategory(config.getCategory("authkey"));
//$$ if (authKey != null) {
//$$ // Note: .get() actually creates the entry with the default value if it doesn't exist
//$$ config.get("authkey", "authkey", authKey);
//$$ }
//$$ config.save();
//$$ }
//$$ }
//#endif

View File

@@ -0,0 +1,69 @@
package com.replaymod.online;
import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.AuthData;
import com.replaymod.online.api.replay.holders.AuthConfirmation;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
/**
* Auth data stored in a plain text file.
*/
public class PlainFileAuthData implements AuthData {
private final Path path;
private String userName;
private String authKey;
public PlainFileAuthData(Path path) {
this.path = path;
}
/**
* Loads the data from the JSON file and checks for its validity.
* If the data is invalid, the JSON file is removed.
* @param apiClient Api client used for validating the auth data
*/
public void load(ApiClient apiClient) throws IOException {
String authKey;
try {
authKey = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
} catch (NoSuchFileException | FileNotFoundException ignored) {
return;
}
AuthConfirmation result = apiClient.checkAuthkey(authKey);
if (result != null) {
this.authKey = authKey;
this.userName = result.getUsername();
} else {
Files.deleteIfExists(path);
}
}
@Override
public String getUserName() {
return userName;
}
@Override
public String getAuthKey() {
return authKey;
}
@Override
public void setData(String userName, String authKey) {
this.userName = userName;
this.authKey = authKey;
try {
Files.write(path, authKey.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE_NEW);
} catch (IOException e) {
ReplayModOnline.LOGGER.error("Saving auth data:", e);
}
}
}

View File

@@ -1,7 +1,10 @@
package com.replaymod.online;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.versions.MCVer;
import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.AuthData;
import com.replaymod.online.gui.GuiLoginPrompt;
import com.replaymod.online.gui.GuiReplayDownloading;
import com.replaymod.online.gui.GuiSaveModifiedReplay;
@@ -12,48 +15,36 @@ import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraftforge.common.config.Configuration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if MC>=10800
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.common.config.Configuration;
//#if MC>=10800
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#else
//$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPostInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
//#endif
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static com.replaymod.core.versions.MCVer.*;
import static net.minecraft.client.Minecraft.getMinecraft;
@Mod(modid = ReplayModOnline.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModOnline {
public static final String MOD_ID = "replaymod-online";
@Mod.Instance(MOD_ID)
public class ReplayModOnline implements Module {
{ instance = this; }
public static ReplayModOnline instance;
private ReplayMod core;
private ReplayModReplay replayModule;
public static Logger LOGGER;
public static Logger LOGGER = LogManager.getLogger();
private ApiClient apiClient;
@@ -64,22 +55,31 @@ public class ReplayModOnline {
*/
private File currentReplayOutputFile;
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
LOGGER = event.getModLog();
core = ReplayMod.instance;
replayModule = ReplayModReplay.instance;
public ReplayModOnline(ReplayMod core, ReplayModReplay replayModule) {
this.core = core;
this.replayModule = replayModule;
core.getSettingsRegistry().register(Setting.class);
Configuration config = new Configuration(event.getSuggestedConfigurationFile());
ConfigurationAuthData authData = new ConfigurationAuthData(config);
apiClient = new ApiClient(authData);
authData.load(apiClient);
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
@Override
public void initClient() {
Path path = MCVer.mcDataDir(MCVer.getMinecraft()).toPath().resolve("config/replaymod-online.token");
PlainFileAuthData authData = new PlainFileAuthData(path);
apiClient = new ApiClient(authData);
if (Files.notExists(path)) {
AuthData oldAuthData = loadOldAuthData();
if (oldAuthData != null) {
authData.setData(oldAuthData.getUserName(), oldAuthData.getAuthKey());
}
} else {
try {
authData.load(apiClient);
} catch (IOException e) {
ReplayModOnline.LOGGER.error("Loading auth data:", e);
}
}
if (!getDownloadsFolder().exists()){
if (!getDownloadsFolder().mkdirs()) {
LOGGER.warn("Failed to create downloads folder: " + getDownloadsFolder());
@@ -88,10 +88,7 @@ public class ReplayModOnline {
new GuiHandler(this).register();
FML_BUS.register(this);
}
@Mod.EventHandler
public void postInit(FMLPostInitializationEvent event) {
// Initial login prompt
if (!core.getSettingsRegistry().get(Setting.SKIP_LOGIN_PROMPT)) {
if (!isLoggedIn()) {
@@ -103,6 +100,23 @@ public class ReplayModOnline {
}
}
/**
* Loads old auth data from the legacy Configuration system and stores it in the new json file.
* Always returns null on 1.13+ where the Configuration system has been removed.
*/
private AuthData loadOldAuthData() {
//#if MC<11300
//$$ Path path = MCVer.mcDataDir(MCVer.getMinecraft()).toPath().resolve("config/replaymod-online.cfg");
//$$ Configuration config = new Configuration(path.toFile());
//$$ ConfigurationAuthData authData = new ConfigurationAuthData(config);
//$$ authData.load(new ApiClient(authData));
//$$ if (authData.getAuthKey() != null) {
//$$ return authData;
//$$ }
//#endif
return null;
}
public ReplayMod getCore() {
return core;
}
@@ -125,7 +139,7 @@ public class ReplayModOnline {
public File getDownloadsFolder() {
String path = core.getSettingsRegistry().get(Setting.DOWNLOAD_PATH);
return new File(path.startsWith("./") ? getMinecraft().mcDataDir : null, path);
return new File(path.startsWith("./") ? mcDataDir(getMinecraft()) : null, path);
}
public File getDownloadedFile(int id) {

View File

@@ -6,6 +6,7 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.versions.MCVer;
import com.replaymod.online.AuthenticationHash;
import com.replaymod.online.api.replay.ReplayModApiMethods;
import com.replaymod.online.api.replay.SearchQuery;
@@ -30,7 +31,7 @@ import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
public class ApiClient {
private static final Minecraft mc = Minecraft.getMinecraft();
private static final Minecraft mc = MCVer.getMinecraft();
private static final Gson gson = new Gson();
private static final JsonParser jsonParser = new JsonParser();

View File

@@ -9,7 +9,8 @@ public enum MinecraftVersion {
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"),
MC_1_12_2("Minecraft 1.12.2", "1.12.2");
MC_1_12_2("Minecraft 1.12.2", "1.12.2"),
MC_1_13_2("Minecraft 1.13.2", "1.13.2");
private String niceName, apiName;

View File

@@ -1,7 +1,6 @@
package com.replaymod.online.gui;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.replaymod.core.versions.MCVer;
import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.ApiException;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
@@ -13,9 +12,9 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.utils.Consumers;
import de.johni0702.minecraft.gui.utils.Utils;
import com.replaymod.core.utils.Patterns;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import net.minecraft.client.gui.FontRenderer;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableColor;
import static com.replaymod.core.versions.MCVer.*;

View File

@@ -37,9 +37,9 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import org.apache.commons.lang3.StringUtils;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import java.awt.image.BufferedImage;
import java.io.IOException;
@@ -462,7 +462,7 @@ public class GuiReplayCenter extends GuiScreen {
@Override
public ReadableDimension calcMinSize(GuiContainer<?> container) {
return new org.lwjgl.util.Dimension(300, thumbnail.getMinSize().getHeight());
return new Dimension(300, thumbnail.getMinSize().getHeight());
}
});
}

View File

@@ -15,9 +15,14 @@ import de.johni0702.minecraft.gui.function.Typeable;
import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadablePoint;
import net.minecraft.client.resources.I18n;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.ReadablePoint;
//#if MC>=11300
import com.replaymod.core.versions.MCVer.Keyboard;
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
import java.util.ArrayList;
import java.util.List;

View File

@@ -33,13 +33,16 @@ import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.resources.I18n;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
//#if MC>=11300
//#else
//$$ import net.minecraft.client.settings.GameSettings;
//#endif
import javax.annotation.Nullable;
import java.awt.image.BufferedImage;
import java.io.File;
@@ -47,6 +50,7 @@ import java.io.IOException;
import java.util.Set;
import java.util.stream.Collectors;
import static com.replaymod.core.versions.MCVer.newReportedException;
import static java.util.Arrays.stream;
public class GuiUploadReplay extends GuiScreen {
@@ -130,7 +134,7 @@ public class GuiUploadReplay extends GuiScreen {
metaData = replayFile.getMetaData();
optThumbnail = replayFile.getThumb();
} catch (IOException e) {
throw new ReportedException(CrashReport.makeCrashReport(e, "Read replay file " + file.getName()));
throw newReportedException(CrashReport.makeCrashReport(e, "Read replay file " + file.getName()));
}
// Apply to gui
@@ -147,7 +151,11 @@ public class GuiUploadReplay extends GuiScreen {
// Get name of key used for thumbnail creation
KeyBindingRegistry registry = mod.getCore().getKeyBindingRegistry();
KeyBinding keyBinding = registry.getKeyBindings().get("replaymod.input.thumbnail");
String keyName = keyBinding == null ? "???" : GameSettings.getKeyDisplayString(keyBinding.getKeyCode());
//#if MC>=11300
String keyName = keyBinding == null ? "???" : keyBinding.func_197978_k();
//#else
//$$ String keyName = keyBinding == null ? "???" : GameSettings.getKeyDisplayString(keyBinding.getKeyCode());
//#endif
// No thumbnail, show default thumbnail and hint on how to create one
thumbnail.setTexture(Utils.DEFAULT_THUMBNAIL);

View File

@@ -15,7 +15,12 @@ import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.GuiScreenEvent;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
import java.util.ArrayList;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
@@ -44,9 +49,21 @@ public class GuiHandler {
return;
}
GuiButton button = new GuiButton(BUTTON_REPLAY_CENTER, getGui(event).width / 2 - 100,
getGui(event).height / 4 + 10 + 4 * 24, I18n.format("replaymod.gui.replaycenter"));
getButtonList(event).add(button);
GuiButton button = new GuiButton(
BUTTON_REPLAY_CENTER,
getGui(event).width / 2 + 2,
getGui(event).height / 4 + 10 + 4 * 24,
I18n.format("replaymod.gui.replaycenter")
) {
//#if MC>=11300
@Override
public void onClick(double mouseX, double mouseY) {
onButton(new GuiScreenEvent.ActionPerformedEvent.Pre(getGui(event), this, new ArrayList<>()));
}
//#endif
};
button.width = button.width / 2 - 2;
addButton(event, button);
}
@SubscribeEvent
@@ -57,22 +74,15 @@ public class GuiHandler {
}
final GuiReplayViewer replayViewer = (GuiReplayViewer) guiScreen;
// Inject Upload button
for (GuiElement element : replayViewer.replayButtonPanel.getChildren()) {
if (element instanceof GuiPanel && (((GuiPanel) element).getChildren().isEmpty())) {
new de.johni0702.minecraft.gui.element.GuiButton((GuiPanel) element).onClick(new Runnable() {
@Override
public void run() {
File replayFile = replayViewer.list.getSelected().file;
GuiUploadReplay uploadGui = new GuiUploadReplay(replayViewer, mod, replayFile);
if (mod.isLoggedIn()) {
uploadGui.display();
} else {
new GuiLoginPrompt(mod.getApiClient(), replayViewer, uploadGui, true);
}
}
}).setSize(73, 20).setI18nLabel("replaymod.gui.upload").setDisabled();
replayViewer.replaySpecificButtons.add(new de.johni0702.minecraft.gui.element.GuiButton(replayViewer.uploadButton).onClick(() -> {
File replayFile = replayViewer.list.getSelected().file;
GuiUploadReplay uploadGui = new GuiUploadReplay(replayViewer, mod, replayFile);
if (mod.isLoggedIn()) {
uploadGui.display();
} else {
new GuiLoginPrompt(mod.getApiClient(), replayViewer, uploadGui, true);
}
}
}).setSize(73, 20).setI18nLabel("replaymod.gui.upload").setDisabled());
}
@SubscribeEvent

View File

@@ -28,8 +28,8 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import java.io.IOException;
import java.util.LinkedHashMap;
@@ -229,6 +229,7 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable {
timelines.putAll(replayFile.getTimelines(registry));
for (Map.Entry<String, Timeline> entry : timelines.entrySet()) {
if (entry.getKey().isEmpty()) continue; // don't show auto-save slot
list.getListPanel().addElements(null, new Entry(entry.getKey()));
}
}

View File

@@ -14,7 +14,11 @@ import com.replaymod.replaystudio.pathing.path.Timeline;
import net.minecraft.client.Minecraft;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
@@ -28,7 +32,7 @@ import static com.replaymod.core.versions.MCVer.*;
* Plays a timeline.
*/
public abstract class AbstractTimelinePlayer {
private final Minecraft mc = Minecraft.getMinecraft();
private final Minecraft mc = getMinecraft();
private final ReplayHandler replayHandler;
private Timeline timeline;
protected long startOffset;

View File

@@ -4,7 +4,11 @@ import com.replaymod.core.utils.WrappedTimer;
import net.minecraft.util.Timer;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.Event;
//#if MC>=11300
import net.minecraftforge.eventbus.api.Event;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.Event;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.Event;
//#endif
@@ -15,16 +19,28 @@ import static com.replaymod.core.versions.MCVer.*;
* Wrapper around the current timer that prevents the timer from advancing by itself.
*/
public class ReplayTimer extends WrappedTimer {
private final Timer state = new Timer(0);
//#if MC>=11300
private final Timer state = new Timer(0, 0);
//#else
//$$ private final Timer state = new Timer(0);
//#endif
public ReplayTimer(Timer wrapped) {
super(wrapped);
}
@Override
public void updateTimer() {
public void updateTimer(
//#if MC>=11300
long sysClock
//#endif
) {
copy(this, state); // Save our current state
super.updateTimer(); // Update current state
wrapped.updateTimer(
//#if MC>=11300
sysClock
//#endif
); // Update current state
copy(state, this); // Restore our old state
FML_BUS.post(new UpdatedEvent());
}

View File

@@ -1,85 +1,99 @@
package com.replaymod.recording;
import com.replaymod.core.KeyBindingRegistry;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Restrictions;
import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.recording.handler.GuiHandler;
import com.replaymod.recording.packet.PacketListener;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler;
import io.netty.util.AttributeKey;
import net.minecraft.network.NetworkManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
//#if MC>=11300
import com.replaymod.core.versions.MCVer.Keyboard;
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus;
import net.minecraftforge.fml.common.network.NetworkRegistry;
//#if MC>=11300
import net.minecraftforge.fml.network.NetworkRegistry;
//#else
//$$ import net.minecraftforge.fml.common.network.NetworkRegistry;
//#endif
//#else
//$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//$$ import cpw.mods.fml.common.eventhandler.EventBus;
//$$ import cpw.mods.fml.common.network.NetworkRegistry;
//#endif
import static com.replaymod.core.versions.MCVer.*;
@Mod(modid = ReplayModRecording.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModRecording {
public static final String MOD_ID = "replaymod-recording";
public class ReplayModRecording implements Module {
@Mod.Instance(MOD_ID)
private static final Logger LOGGER = LogManager.getLogger();
//#if MC>=11300
private static final AttributeKey<Void> ATTR_CHECKED = AttributeKey.newInstance("ReplayModRecording_checked");
//#endif
{ instance = this; }
public static ReplayModRecording instance;
private ReplayMod core;
private Logger logger;
private ConnectionEventHandler connectionEventHandler;
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
logger = event.getModLog();
core = ReplayMod.instance;
public ReplayModRecording(ReplayMod mod) {
core = mod;
core.getSettingsRegistry().register(Setting.class);
}
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.marker", Keyboard.KEY_M, new Runnable() {
@Override
public void registerKeyBindings(KeyBindingRegistry registry) {
registry.registerKeyBinding("replaymod.input.marker", Keyboard.KEY_M, new Runnable() {
@Override
public void run() {
PacketListener packetListener = connectionEventHandler.getPacketListener();
if (packetListener != null) {
packetListener.addMarker();
packetListener.addMarker(null);
core.printInfoToChat("replaymod.chat.addedmarker");
}
}
});
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
EventBus bus = FML_BUS;
bus.register(connectionEventHandler = new ConnectionEventHandler(logger, core));
@Override
public void initClient() {
FML_BUS.register(connectionEventHandler = new ConnectionEventHandler(LOGGER, core));
new GuiHandler(core).register();
NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler());
//#if MC>=11300
NetworkRegistry.newEventChannel(Restrictions.PLUGIN_CHANNEL, () -> "0", any -> true, any -> true);
//#else
//$$ NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler());
//#endif
}
@ChannelHandler.Sharable
private static class RestrictionsChannelHandler extends ChannelDuplexHandler {}
public void initiateRecording(NetworkManager networkManager) {
Channel channel = networkManager.channel();
if (channel.pipeline().get("ReplayModReplay_replaySender") != null) return;
//#if MC>=11300
if (channel.hasAttr(ATTR_CHECKED)) return;
channel.attr(ATTR_CHECKED).set(null);
//#endif
connectionEventHandler.onConnectedToServerEvent(networkManager);
}
public ConnectionEventHandler getConnectionEventHandler() {
return connectionEventHandler;
}
}

View File

@@ -6,12 +6,14 @@ public final class Setting<T> extends SettingsRegistry.SettingKeys<T> {
public static final Setting<Boolean> RECORD_SINGLEPLAYER = make("recordSingleplayer", "recordsingleplayer", true);
public static final Setting<Boolean> RECORD_SERVER = make("recordServer", "recordserver", true);
public static final Setting<Boolean> INDICATOR = make("indicator", "indicator", true);
public static final Setting<Boolean> AUTO_START_RECORDING = make("autoStartRecording", "autostartrecording", true);
public static final Setting<Boolean> AUTO_POST_PROCESS = make("autoPostProcess", null, true);
private static <T> Setting<T> make(String key, String displayName, T defaultValue) {
return new Setting<>(key, displayName, defaultValue);
}
public Setting(String key, String displayString, T defaultValue) {
super("recording", key, "replaymod.gui.settings." + displayString, defaultValue);
super("recording", key, displayString == null ? null : "replaymod.gui.settings." + displayString, defaultValue);
}
}

View File

@@ -0,0 +1,110 @@
package com.replaymod.recording.gui;
import com.replaymod.core.ReplayMod;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.Setting;
import com.replaymod.recording.packet.PacketListener;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.container.VanillaGuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import net.minecraft.client.gui.GuiIngameMenu;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
import static com.replaymod.core.versions.MCVer.getGui;
public class GuiRecordingControls {
private ReplayMod core;
private PacketListener packetListener;
private boolean paused;
private boolean stopped;
private GuiPanel panel = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(4));
private GuiButton buttonPauseResume = new GuiButton(panel).onClick(() -> {
if (paused) {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
} else {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT);
}
paused = !paused;
updateState();
}).setSize(98, 20);
private GuiButton buttonStartStop = new GuiButton(panel).onClick(() -> {
if (stopped) {
paused = false;
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
core.printInfoToChat("replaymod.chat.recordingstarted");
} else {
int timestamp = (int) packetListener.getCurrentDuration();
if (!paused) {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT, timestamp);
}
packetListener.addMarker(MarkerProcessor.MARKER_NAME_SPLIT, timestamp + 1);
}
stopped = !stopped;
updateState();
}).setSize(98, 20);
public GuiRecordingControls(ReplayMod core, PacketListener packetListener) {
this.core = core;
this.packetListener = packetListener;
paused = stopped = !core.getSettingsRegistry().get(Setting.AUTO_START_RECORDING);
updateState();
}
public void register() {
MinecraftForge.EVENT_BUS.register(this);
}
public void unregister() {
MinecraftForge.EVENT_BUS.unregister(this);
}
@SubscribeEvent
public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) {
if (getGui(event) instanceof GuiIngameMenu) {
show((GuiIngameMenu) getGui(event));
}
}
private void updateState() {
buttonPauseResume.setI18nLabel("replaymod.gui.recording." + (paused ? "resume" : "pause"));
buttonStartStop.setI18nLabel("replaymod.gui.recording." + (stopped ? "start" : "stop"));
buttonPauseResume.setEnabled(!stopped);
}
public void show(GuiIngameMenu gui) {
VanillaGuiScreen.setup(gui).setLayout(new CustomLayout<GuiScreen>() {
@Override
protected void layout(GuiScreen container, int width, int height) {
pos(panel, width / 2 - 100, height / 4 + 128);
}
}).addElements(null, panel);
}
public boolean isPaused() {
return paused;
}
public boolean isStopped() {
return stopped;
}
}

View File

@@ -10,10 +10,15 @@ import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800
import net.minecraft.client.renderer.GlStateManager;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import static net.minecraft.client.renderer.GlStateManager.*;
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//$$ import static com.replaymod.core.versions.MCVer.GlStateManager.*;
//#endif
import static com.replaymod.core.ReplayMod.TEXTURE;
@@ -26,10 +31,12 @@ import static com.replaymod.core.versions.MCVer.*;
public class GuiRecordingOverlay {
private final Minecraft mc;
private final SettingsRegistry settingsRegistry;
private final GuiRecordingControls guiControls;
public GuiRecordingOverlay(Minecraft mc, SettingsRegistry settingsRegistry) {
public GuiRecordingOverlay(Minecraft mc, SettingsRegistry settingsRegistry, GuiRecordingControls guiControls) {
this.mc = mc;
this.settingsRegistry = settingsRegistry;
this.guiControls = guiControls;
}
public void register() {
@@ -47,12 +54,14 @@ public class GuiRecordingOverlay {
@SubscribeEvent
public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) {
if (getType(event) != RenderGameOverlayEvent.ElementType.ALL) return;
if (guiControls.isStopped()) return;
if (settingsRegistry.get(Setting.INDICATOR)) {
FontRenderer fontRenderer = getFontRenderer(mc);
fontRenderer.drawString(I18n.format("replaymod.gui.recording").toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff);
mc.renderEngine.bindTexture(TEXTURE);
GlStateManager.resetColor();
GlStateManager.enableAlpha();
String text = guiControls.isPaused() ? I18n.format("replaymod.gui.paused") : I18n.format("replaymod.gui.recording");
fontRenderer.drawString(text.toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff);
bindTexture(TEXTURE);
resetColor();
enableAlpha();
Gui.drawModalRectWithCustomSizedTexture(10, 10, 58, 20, 16, 16, TEXTURE_SIZE, TEXTURE_SIZE);
}
}

View File

@@ -3,7 +3,9 @@ package com.replaymod.recording.handler;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.ModCompat;
import com.replaymod.core.utils.Utils;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.Setting;
import com.replaymod.recording.gui.GuiRecordingControls;
import com.replaymod.recording.gui.GuiRecordingOverlay;
import com.replaymod.recording.packet.PacketListener;
import com.replaymod.replaystudio.replay.ReplayFile;
@@ -15,19 +17,19 @@ import net.minecraft.network.NetworkManager;
import org.apache.logging.log4j.Logger;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
//#if MC>=11300
import net.minecraft.world.dimension.DimensionType;
//#endif
import static com.replaymod.core.versions.MCVer.WorldType_DEBUG_ALL_BLOCK_STATES;
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//$$ import cpw.mods.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
//#endif
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import static com.replaymod.core.versions.MCVer.getMinecraft;
/**
* Handles connection events and initiates recording if enabled.
*/
@@ -36,7 +38,7 @@ public class ConnectionEventHandler {
private static final String packetHandlerKey = "packet_handler";
private static final String DATE_FORMAT = "yyyy_MM_dd_HH_mm_ss";
private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
private static final Minecraft mc = Minecraft.getMinecraft();
private static final Minecraft mc = getMinecraft();
private final Logger logger;
private final ReplayMod core;
@@ -44,6 +46,7 @@ public class ConnectionEventHandler {
private RecordingEventHandler recordingEventHandler;
private PacketListener packetListener;
private GuiRecordingOverlay guiOverlay;
private GuiRecordingControls guiControls;
public ConnectionEventHandler(Logger logger, ReplayMod core) {
this.logger = logger;
@@ -55,7 +58,11 @@ public class ConnectionEventHandler {
boolean local = networkManager.isLocalChannel();
if (local) {
//#if MC>=10800
if (mc.getIntegratedServer().getEntityWorld().getWorldType() == WorldType_DEBUG_ALL_BLOCK_STATES) {
//#if MC>=11300
if (mc.getIntegratedServer().getWorld(DimensionType.OVERWORLD).getWorldType() == WorldType_DEBUG_ALL_BLOCK_STATES) {
//#else
//$$ if (mc.getIntegratedServer().getEntityWorld().getWorldType() == WorldType_DEBUG_ALL_BLOCK_STATES) {
//#endif
logger.info("Debug World recording is not supported.");
return;
}
@@ -74,10 +81,10 @@ public class ConnectionEventHandler {
String worldName;
if (local) {
worldName = mc.getIntegratedServer().getWorldName();
} else if (Minecraft.getMinecraft().getCurrentServerData() != null) {
worldName = Minecraft.getMinecraft().getCurrentServerData().serverIP;
} else if (mc.getCurrentServerData() != null) {
worldName = mc.getCurrentServerData().serverIP;
//#if MC>=11100
} else if (Minecraft.getMinecraft().isConnectedToRealms()) {
} else if (mc.isConnectedToRealms()) {
// we can't access the server name without tapping too deep in the Realms Library
worldName = "A Realms Server";
//#endif
@@ -97,28 +104,36 @@ public class ConnectionEventHandler {
ReplayMetaData metaData = new ReplayMetaData();
metaData.setSingleplayer(local);
metaData.setServerName(worldName);
metaData.setGenerator("ReplayMod v" + ReplayMod.getContainer().getVersion());
metaData.setGenerator("ReplayMod v" + ReplayMod.instance.getVersion());
metaData.setDate(System.currentTimeMillis());
metaData.setMcVersion(ReplayMod.getMinecraftVersion());
packetListener = new PacketListener(replayFile, metaData);
packetListener = new PacketListener(core, currentFile.toPath(), replayFile, metaData);
networkManager.channel().pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener);
recordingEventHandler = new RecordingEventHandler(packetListener);
recordingEventHandler.register();
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry());
guiControls = new GuiRecordingControls(core, packetListener);
guiControls.register();
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry(), guiControls);
guiOverlay.register();
core.printInfoToChat("replaymod.chat.recordingstarted");
if (core.getSettingsRegistry().get(Setting.AUTO_START_RECORDING)) {
core.printInfoToChat("replaymod.chat.recordingstarted");
} else {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT, 0);
}
} catch (Throwable e) {
e.printStackTrace();
core.printWarningToChat("replaymod.chat.recordingfailed");
}
}
@SubscribeEvent
public void onDisconnectedFromServerEvent(ClientDisconnectionFromServerEvent event) {
public void reset() {
if (packetListener != null) {
guiControls.unregister();
guiControls = null;
guiOverlay.unregister();
guiOverlay = null;
recordingEventHandler.unregister();

View File

@@ -1,34 +1,36 @@
package com.replaymod.recording.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
//#if MC<11300
//$$ package com.replaymod.recording.handler;
//$$
//$$ import io.netty.channel.ChannelHandlerContext;
//$$ import io.netty.channel.SimpleChannelInboundHandler;
//$$
//#if MC>=10800
import net.minecraftforge.fml.common.network.handshake.FMLHandshakeMessage;
//$$ import net.minecraftforge.fml.common.network.handshake.FMLHandshakeMessage;
//#else
//$$ import cpw.mods.fml.common.network.handshake.FMLHandshakeMessage;
//#endif
/**
* Filters out all handshake packets that were sent for recording but must
* not actually be handled.
* This handler is only present when connected to the integrated server as
* otherwise all packets must be handled.
*
* When in single player, the game state packets must never be handled
* otherwise wired bugs related to semi-singletons can occur.
* See https://bugs.replaymod.com/show_bug.cgi?id=44
*/
public class FMLHandshakeFilter extends SimpleChannelInboundHandler<FMLHandshakeMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception {
//$$
//$$ /**
//$$ * Filters out all handshake packets that were sent for recording but must
//$$ * not actually be handled.
//$$ * This handler is only present when connected to the integrated server as
//$$ * otherwise all packets must be handled.
//$$ *
//$$ * When in single player, the game state packets must never be handled
//$$ * otherwise wired bugs related to semi-singletons can occur.
//$$ * See https://bugs.replaymod.com/show_bug.cgi?id=44
//$$ */
//$$ public class FMLHandshakeFilter extends SimpleChannelInboundHandler<FMLHandshakeMessage> {
//$$ @Override
//$$ protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception {
//#if MC>=10800
if (!(msg instanceof FMLHandshakeMessage.RegistryData)) {
//$$ if (!(msg instanceof FMLHandshakeMessage.RegistryData)) {
//#else
//$$ if (!(msg instanceof FMLHandshakeMessage.ModIdData)) {
//#endif
// Pass on everything but RegistryData messages
ctx.fireChannelRead(msg);
}
}
}
//$$ // Pass on everything but RegistryData messages
//$$ ctx.fireChannelRead(msg);
//$$ }
//$$ }
//$$ }
//#endif

View File

@@ -17,7 +17,11 @@ import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif

View File

@@ -7,11 +7,15 @@ import net.minecraft.item.ItemStack;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.*;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraftforge.event.entity.minecart.MinecartInteractEvent;
// FIXME not (yet?) 1.13 import net.minecraftforge.event.entity.minecart.MinecartInteractEvent;
import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent;
//#if MC>=10904
import net.minecraft.entity.EntityLiving;
//#if MC>=11300
import net.minecraft.entity.EntityLivingBase;
//#else
//$$ import net.minecraft.entity.EntityLiving;
//#endif
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.EnumHand;
@@ -25,7 +29,11 @@ import net.minecraft.util.math.BlockPos;
//#endif
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent;
@@ -42,7 +50,7 @@ import static com.replaymod.core.versions.MCVer.*;
public class RecordingEventHandler {
private final Minecraft mc = Minecraft.getMinecraft();
private final Minecraft mc = getMinecraft();
private final PacketListener packetListener;
private Double lastX, lastY, lastZ;
@@ -75,6 +83,12 @@ public class RecordingEventHandler {
}
}
//#if MC>=11300
public void onPacket(Packet<?> packet) {
packetListener.save(packet);
}
//#endif
public void onPlayerJoin() {
try {
//#if MC>=10904
@@ -171,9 +185,15 @@ public class RecordingEventHandler {
byte newPitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F));
//#if MC>=10904
packet = new SPacketEntity.S17PacketEntityLookMove(e.player.getEntityId(),
//#if MC>=11300
packet = new SPacketEntity.Move(
//#else
//$$ packet = new SPacketEntity.S17PacketEntityLookMove(
//#endif
e.player.getEntityId(),
(short) Math.round(dx * 4096), (short) Math.round(dy * 4096), (short) Math.round(dz * 4096),
newYaw, newPitch, e.player.onGround);
newYaw, newPitch, e.player.onGround
);
//#else
//$$ packet = new S14PacketEntity.S17PacketEntityLookMove(e.player.getEntityId(),
//$$ (byte) Math.round(dx * 32), (byte) Math.round(dy * 32), (byte) Math.round(dz * 32),
@@ -300,9 +320,15 @@ public class RecordingEventHandler {
//Leaving Ride
if((!player(mc).isRiding() && lastRiding != -1) ||
(player(mc).isRiding() && lastRiding != getRidingEntity(player(mc)).getEntityId())) {
if(!player(mc).isRiding()) {
//#if MC>=11300
if((!player(mc).isPassenger() && lastRiding != -1) ||
(player(mc).isPassenger() && lastRiding != getRidingEntity(player(mc)).getEntityId())) {
if(!player(mc).isPassenger()) {
//#else
//$$ if((!player(mc).isRiding() && lastRiding != -1) ||
//$$ (player(mc).isRiding() && lastRiding != getRidingEntity(player(mc)).getEntityId())) {
//$$ if(!player(mc).isRiding()) {
//#endif
lastRiding = -1;
} else {
lastRiding = getRidingEntity(player(mc)).getEntityId();
@@ -331,7 +357,11 @@ public class RecordingEventHandler {
lastActiveHand = player(mc).getActiveHand();
EntityDataManager dataManager = new EntityDataManager(null);
int state = (wasHandActive ? 1 : 0) | (lastActiveHand == EnumHand.OFF_HAND ? 2 : 0);
dataManager.register(EntityLiving.HAND_STATES, (byte) state);
//#if MC>=11300
dataManager.register(EntityLivingBase.LIVING_FLAGS, (byte) state);
//#else
//$$ dataManager.register(EntityLiving.HAND_STATES, (byte) state);
//#endif
packetListener.save(new SPacketEntityMetadata(player(mc).getEntityId(), dataManager, true));
}
//#endif
@@ -346,8 +376,17 @@ public class RecordingEventHandler {
try {
//#if MC>=11100
//#if MC>=11200
packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
event.pickedUp.getItem().getMaxStackSize()));
//#if MC>=11300
ItemStack stack = event.getStack();
packetListener.save(new SPacketCollectItem(
event.getOriginalEntity().getEntityId(),
event.getPlayer().getEntityId(),
event.getStack().getCount()
));
//#else
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
//$$ event.pickedUp.getItem().getMaxStackSize()));
//#endif
//#else
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
//$$ event.pickedUp.getEntityItem().getMaxStackSize()));
@@ -394,6 +433,7 @@ public class RecordingEventHandler {
}
}
/* FIXME event not (yet?) on 1.13
@SubscribeEvent
public void enterMinecart(MinecartInteractEvent event) {
try {
@@ -418,6 +458,7 @@ public class RecordingEventHandler {
e.printStackTrace();
}
}
*/
//#if MC>=10800
public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) {

View File

@@ -0,0 +1,36 @@
//#if MC>=11300
package com.replaymod.recording.mixin;
import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.recording.packet.ResourcePackRecorder;
import de.johni0702.minecraft.gui.utils.Consumer;
import net.minecraft.client.resources.DownloadingPackFinder;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.io.File;
@Mixin(DownloadingPackFinder.class)
public abstract class MixinDownloadingPackFinder implements ResourcePackRecorder.IDownloadingPackFinder {
private Consumer<File> requestCallback;
@Override
public void setRequestCallback(Consumer<File> callback) {
requestCallback = callback;
}
@Shadow
public abstract ListenableFuture<Object> func_195741_a(File file);
@Redirect(method = "downloadResourcePack", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/DownloadingPackFinder;func_195741_a(Ljava/io/File;)Lcom/google/common/util/concurrent/ListenableFuture;"))
private ListenableFuture<Object> recordDownloadedPack(DownloadingPackFinder downloadingPackFinder, File file) {
if (requestCallback != null) {
requestCallback.consume(file);
requestCallback = null;
}
return func_195741_a(file);
}
}
//#endif

View File

@@ -0,0 +1,45 @@
//#if MC>=11300
package com.replaymod.recording.mixin;
import com.replaymod.core.versions.MCVer;
import com.replaymod.extras.advancedscreenshots.AdvancedScreenshots;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.KeyboardListener;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.client.shader.Framebuffer;
import net.minecraft.util.ScreenShotHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.fml.hooks.BasicEventHooks;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.File;
import java.util.function.Consumer;
@Mixin(KeyboardListener.class)
public abstract class MixinKeyboardListener {
@Inject(method = "onKeyEvent", at = @At("HEAD"), cancellable = true)
private void dispatchKeyEvent(long windowPointer, int key, int scanCode, int action, int modifiers, CallbackInfo ci) {
// FIXME this is a hack and will hopefully be done properly by forge before its release (MinecraftForge#5481)
// (and therefore before RM's release), this code will have to be removed then and handling code updated
KeyBinding keyBindChat = MCVer.getMinecraft().gameSettings.keyBindChat;
if (action == 1 && keyBindChat.matchesKey(key, scanCode)) {
keyBindChat.pressTime++;
BasicEventHooks.fireKeyInput();
ci.cancel();
}
}
@Redirect(method = "onKeyEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/ScreenShotHelper;saveScreenshot(Ljava/io/File;IILnet/minecraft/client/shader/Framebuffer;Ljava/util/function/Consumer;)V"))
private void takeScreenshot(File p_148260_0_, int p_148260_1_, int p_148260_2_, Framebuffer p_148260_3_, Consumer<ITextComponent> p_148260_4_) {
if (ReplayModReplay.instance.getReplayHandler() != null) {
AdvancedScreenshots.take();
} else {
ScreenShotHelper.saveScreenshot(p_148260_0_, p_148260_1_, p_148260_2_, p_148260_3_, p_148260_4_);
}
}
}
//#endif

View File

@@ -0,0 +1,41 @@
//#if MC>=11300
package com.replaymod.recording.mixin;
import com.replaymod.replay.InputReplayTimer;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.MouseHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(MouseHelper.class)
public abstract class MixinMouseHelper {
//#ifdef DEV_ENV
@Shadow
private boolean mouseGrabbed;
@Inject(method = "grabMouse", at = @At("HEAD"), cancellable = true)
private void noGrab(CallbackInfo ci) {
// Used to be provided by Forge for 1.12.2 and below
if (Boolean.valueOf(System.getProperty("fml.noGrab", "false"))) {
mouseGrabbed = true;
ci.cancel();
}
}
//#endif
@Inject(method = "scrollCallback",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/entity/EntityPlayerSP;isSpectator()Z"),
locals = LocalCapture.CAPTURE_FAILHARD,
cancellable = true)
private void handleReplayModScroll(long _p0, double _p1, double _p2, CallbackInfo ci, double _l1, double yOffsetAccumulated) {
if (ReplayModReplay.instance.getReplayHandler() != null) {
InputReplayTimer.handleScroll((int) yOffsetAccumulated);
ci.cancel();
}
}
}
//#endif

View File

@@ -9,11 +9,17 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11300
import com.replaymod.core.versions.MCVer;
import com.replaymod.recording.handler.RecordingEventHandler.RecordingEventSender;
import net.minecraft.network.login.server.SPacketCustomPayloadLogin;
//#else
//#if MC>=10800
import net.minecraftforge.fml.common.network.FMLNetworkEvent;
//$$ import net.minecraftforge.fml.common.network.FMLNetworkEvent;
//#else
//$$ import cpw.mods.fml.common.network.FMLNetworkEvent;
//#endif
//#endif
@Mixin(NetHandlerLoginClient.class)
public abstract class MixinNetHandlerLoginClient {
@@ -25,19 +31,33 @@ public abstract class MixinNetHandlerLoginClient {
//$$ private NetworkManager field_147393_d;
//#endif
/**
* Starts the recording right before switching into PLAY state.
* We cannot use the {@link FMLNetworkEvent.ClientConnectedToServerEvent}
* as it only fires after the forge handshake.
*/
@Inject(method = "handleLoginSuccess", at=@At("HEAD"))
public void replayModRecording_initiateRecording(CallbackInfo cb) {
//#if MC>=10800
//#if MC>=11300
@Inject(method = "func_209521_a", at=@At("HEAD"))
private void replayModRecording_initiateRecording(SPacketCustomPayloadLogin packet, CallbackInfo ci) {
RecordingEventSender eventSender = (RecordingEventSender) MCVer.getMinecraft().renderGlobal;
if (eventSender.getRecordingEventHandler() != null) {
return; // already recording
}
ReplayModRecording.instance.initiateRecording(networkManager);
if (eventSender.getRecordingEventHandler() != null) {
eventSender.getRecordingEventHandler().onPacket(packet);
}
}
//#else
//$$ /**
//$$ * Starts the recording right before switching into PLAY state.
//$$ * We cannot use the {@link FMLNetworkEvent.ClientConnectedToServerEvent}
//$$ * as it only fires after the forge handshake.
//$$ */
//$$ @Inject(method = "handleLoginSuccess", at=@At("HEAD"))
//$$ public void replayModRecording_initiateRecording(CallbackInfo cb) {
//#if MC>=10800
//$$ ReplayModRecording.instance.initiateRecording(networkManager);
//#else
//$$ ReplayModRecording.instance.initiateRecording(field_147393_d);
//#endif
}
//$$ }
//#endif
//#if MC>=11200
@Inject(method = "handleLoginSuccess", at=@At("RETURN"))

View File

@@ -1,5 +1,6 @@
package com.replaymod.recording.mixin;
import com.replaymod.core.versions.MCVer;
import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.network.NetHandlerPlayClient;
@@ -30,8 +31,7 @@ import static com.replaymod.core.versions.MCVer.*;
@Mixin(NetHandlerPlayClient.class)
public abstract class MixinNetHandlerPlayClient {
@Shadow
private Minecraft gameController;
private static Minecraft gameController = MCVer.getMinecraft();
//#if MC>=10800
@Shadow

View File

@@ -1,75 +1,77 @@
package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.FMLHandshakeFilter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.embedded.EmbeddedChannel;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC<11300
//$$ package com.replaymod.recording.mixin;
//$$
//$$ import com.replaymod.recording.handler.FMLHandshakeFilter;
//$$ import io.netty.channel.ChannelPipeline;
//$$ import io.netty.channel.embedded.EmbeddedChannel;
//$$ import org.spongepowered.asm.mixin.Mixin;
//$$ import org.spongepowered.asm.mixin.Shadow;
//$$ import org.spongepowered.asm.mixin.injection.At;
//$$ import org.spongepowered.asm.mixin.injection.Inject;
//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//$$
//#if MC>=11200
import io.netty.channel.ChannelConfig;
import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.NetworkManager;
import org.spongepowered.asm.mixin.injection.Redirect;
//$$ import io.netty.channel.ChannelConfig;
//$$ import net.minecraft.network.EnumConnectionState;
//$$ import net.minecraft.network.NetworkManager;
//$$ import org.spongepowered.asm.mixin.injection.Redirect;
//#endif
//$$
//#if MC>=10800
import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import net.minecraftforge.fml.relauncher.Side;
//$$ import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec;
//$$ import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
//$$ import net.minecraftforge.fml.relauncher.Side;
//#else
//$$ import cpw.mods.fml.common.network.handshake.FMLHandshakeCodec;
//$$ import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
//$$ import cpw.mods.fml.relauncher.Side;
//#endif
@Mixin(value = NetworkDispatcher.class, remap = false)
public abstract class MixinNetworkDispatcher {
@Shadow
private Side side;
@Shadow
private EmbeddedChannel handshakeChannel;
/**
* Always sets fml:isLocal to false on the server side.
* This effectively removes the difference in the FML handshake between SP and MP
* and forces the block/item ids, etc. to always be send.
* Injects a {@link FMLHandshakeFilter} on the client side to filter out
* those extra, unexpected packets.
*/
@Inject(method = "insertIntoChannel", at=@At("HEAD"))
public void replayModRecording_setupForLocalRecording(CallbackInfo cb) {
// If we're in multiplayer, everything is fine as is
if (!handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).get()) return;
if (side == Side.SERVER) {
// On the server side, force all packets to be sent
handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false);
} else {
// On the client side, discard additional packets
ChannelPipeline pipeline = handshakeChannel.pipeline();
pipeline.addAfter(pipeline.context(FMLHandshakeCodec.class).name(),
"replaymod_filter", new FMLHandshakeFilter());
}
}
//$$
//$$ @Mixin(value = NetworkDispatcher.class, remap = false)
//$$ public abstract class MixinNetworkDispatcher {
//$$
//$$ @Shadow
//$$ private Side side;
//$$
//$$ @Shadow
//$$ private EmbeddedChannel handshakeChannel;
//$$
//$$ /**
//$$ * Always sets fml:isLocal to false on the server side.
//$$ * This effectively removes the difference in the FML handshake between SP and MP
//$$ * and forces the block/item ids, etc. to always be send.
//$$ * Injects a {@link FMLHandshakeFilter} on the client side to filter out
//$$ * those extra, unexpected packets.
//$$ */
//$$ @Inject(method = "insertIntoChannel", at=@At("HEAD"))
//$$ public void replayModRecording_setupForLocalRecording(CallbackInfo cb) {
//$$ // If we're in multiplayer, everything is fine as is
//$$ if (!handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).get()) return;
//$$
//$$ if (side == Side.SERVER) {
//$$ // On the server side, force all packets to be sent
//$$ handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false);
//$$ } else {
//$$ // On the client side, discard additional packets
//$$ ChannelPipeline pipeline = handshakeChannel.pipeline();
//$$ pipeline.addAfter(pipeline.context(FMLHandshakeCodec.class).name(),
//$$ "replaymod_filter", new FMLHandshakeFilter());
//$$ }
//$$ }
//$$
//#if MC>=11200
@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);
}
//$$ @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);
//$$ }
//#endif
}
//$$ }
//#endif

View File

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

View File

@@ -1,12 +1,17 @@
package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.renderer.RenderGlobal;
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.CallbackInfo;
//#if MC>=11300
import net.minecraft.client.renderer.WorldRenderer;
//#else
//$$ import net.minecraft.client.renderer.RenderGlobal;
//#endif
//#if MC>=10904
import net.minecraft.util.math.BlockPos;
//#else
@@ -15,7 +20,11 @@ import net.minecraft.util.math.BlockPos;
//#endif
//#endif
@Mixin(RenderGlobal.class)
//#if MC>=11300
@Mixin(WorldRenderer.class)
//#else
//$$ @Mixin(RenderGlobal.class)
//#endif
public abstract class MixinRenderGlobal implements RecordingEventHandler.RecordingEventSender {
private RecordingEventHandler recordingEventHandler;

View File

@@ -10,7 +10,6 @@ import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.storage.ISaveHandler;
import net.minecraft.world.storage.WorldInfo;
import org.spongepowered.asm.mixin.Mixin;
@@ -19,6 +18,13 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11300
import net.minecraft.world.dimension.Dimension;
import net.minecraft.world.storage.WorldSavedDataStorage;
//#else
//$$ import net.minecraft.world.WorldProvider;
//#endif
import static com.replaymod.core.versions.MCVer.*;
@Mixin(WorldClient.class)
@@ -26,8 +32,22 @@ public abstract class MixinWorldClient extends World implements RecordingEventHa
@Shadow
private Minecraft mc;
protected MixinWorldClient(ISaveHandler saveHandlerIn, WorldInfo info, WorldProvider providerIn, Profiler profilerIn, boolean client) {
super(saveHandlerIn, info, providerIn, profilerIn, client);
protected MixinWorldClient(ISaveHandler saveHandlerIn,
//#if MC>=11300
WorldSavedDataStorage mapStorage,
//#endif
WorldInfo info,
//#if MC>=11300
Dimension providerIn,
//#else
//$$ WorldProvider providerIn,
//#endif
Profiler profilerIn, boolean client) {
super(saveHandlerIn,
//#if MC>=11300
mapStorage,
//#endif
info, providerIn, profilerIn, client);
}
private RecordingEventHandler replayModRecording_getRecordingEventHandler() {

View File

@@ -1,9 +1,16 @@
package com.replaymod.recording.packet;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Restrictions;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.ReplayModRecording;
import com.replaymod.recording.Setting;
import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.utils.Colors;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
@@ -17,6 +24,10 @@ import net.minecraft.network.play.server.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if MC>=11300
import net.minecraft.network.login.server.SPacketLoginSuccess;
//#endif
//#if MC>=10904
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.text.TextComponentString;
@@ -29,13 +40,16 @@ import net.minecraft.util.text.TextComponentString;
//#if MC>=10800
import net.minecraft.network.EnumPacketDirection;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
//#if MC<11300
//$$ import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
//#endif
//#else
//$$ import cpw.mods.fml.common.network.internal.FMLProxyPacket;
//#endif
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -49,9 +63,11 @@ import static com.replaymod.core.versions.MCVer.*;
public class PacketListener extends ChannelInboundHandlerAdapter {
private static final Minecraft mc = Minecraft.getMinecraft();
private static final Minecraft mc = getMinecraft();
private static final Logger logger = LogManager.getLogger();
private final ReplayMod core;
private final Path outputPath;
private final ReplayFile replayFile;
private final ResourcePackRecorder resourcePackRecorder;
@@ -67,6 +83,11 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
private long lastSentPacket;
private long timePassedWhilePaused;
private volatile boolean serverWasPaused;
//#if MC>=11300
private EnumConnectionState connectionState = EnumConnectionState.LOGIN;
//#else
//$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY;
//#endif
/**
* Used to keep track of the last metadata save job submitted to the save service and
@@ -74,7 +95,9 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
*/
private final AtomicInteger lastSaveMetaDataId = new AtomicInteger();
public PacketListener(ReplayFile replayFile, ReplayMetaData metaData) throws IOException {
public PacketListener(ReplayMod core, Path outputPath, ReplayFile replayFile, ReplayMetaData metaData) throws IOException {
this.core = core;
this.outputPath = outputPath;
this.replayFile = replayFile;
this.metaData = metaData;
this.resourcePackRecorder = new ResourcePackRecorder(replayFile);
@@ -147,6 +170,12 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
throw new RuntimeException(e);
}
});
//#if MC>=11300
if (packet instanceof SPacketLoginSuccess) {
connectionState = EnumConnectionState.PLAY;
}
//#endif
} catch(Exception e) {
logger.error("Writing packet:", e);
}
@@ -157,21 +186,39 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
metaData.setDuration((int) lastSentPacket);
saveMetaData();
saveService.shutdown();
try {
saveService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.error("Waiting for save service termination:", e);
}
synchronized (replayFile) {
try {
replayFile.save();
replayFile.close();
} catch (IOException e) {
logger.error("Saving replay file:", e);
core.runLater(() -> {
ConnectionEventHandler connectionEventHandler = ReplayModRecording.instance.getConnectionEventHandler();
if (connectionEventHandler.getPacketListener() == this) {
connectionEventHandler.reset();
}
}
});
GuiLabel savingLabel = new GuiLabel().setI18nText("replaymod.gui.replaysaving.title").setColor(Colors.BLACK);
new Thread(() -> {
core.runLater(() -> core.getBackgroundProcesses().addProcess(savingLabel));
saveService.shutdown();
try {
saveService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.error("Waiting for save service termination:", e);
}
synchronized (replayFile) {
try {
replayFile.save();
replayFile.close();
if (core.getSettingsRegistry().get(Setting.AUTO_POST_PROCESS)) {
MarkerProcessor.apply(outputPath, progress -> {});
}
} catch (IOException e) {
logger.error("Saving replay file:", e);
}
}
core.runLater(() -> core.getBackgroundProcesses().removeProcess(savingLabel));
}).start();
}
@Override
@@ -229,16 +276,18 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
//#endif
//#endif
if (packet instanceof FMLProxyPacket) {
// This packet requires special handling
//#if MC<11300
//$$ if (packet instanceof FMLProxyPacket) {
//$$ // This packet requires special handling
//#if MC>=10800
((FMLProxyPacket) packet).toS3FPackets().forEach(this::save);
//$$ ((FMLProxyPacket) packet).toS3FPackets().forEach(this::save);
//#else
//$$ save(((FMLProxyPacket) packet).toS3FPacket());
//#endif
super.channelRead(ctx, msg);
return;
}
//$$ super.channelRead(ctx, msg);
//$$ return;
//$$ }
//#endif
save(packet);
@@ -337,9 +386,9 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
//#endif
//#if MC>=10800
Integer packetId = EnumConnectionState.PLAY.getPacketId(EnumPacketDirection.CLIENTBOUND, packet);
Integer packetId = connectionState.getPacketId(EnumPacketDirection.CLIENTBOUND, packet);
//#else
//$$ Integer packetId = (Integer) EnumConnectionState.PLAY.func_150755_b().inverse().get(packet.getClass());
//$$ Integer packetId = (Integer) connectionState.func_150755_b().inverse().get(packet.getClass());
//#endif
if (packetId == null) {
throw new IOException("Unknown packet type:" + packet.getClass());
@@ -357,11 +406,15 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
return array;
}
public void addMarker() {
Entity view = getRenderViewEntity(Minecraft.getMinecraft());
int timestamp = (int) (System.currentTimeMillis() - startTime);
public void addMarker(String name) {
addMarker(name, (int) getCurrentDuration());
}
public void addMarker(String name, int timestamp) {
Entity view = getRenderViewEntity(mc);
Marker marker = new Marker();
marker.setName(name);
marker.setTime(timestamp);
marker.setX(view.posX);
marker.setY(view.posY);
@@ -382,6 +435,10 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
});
}
public long getCurrentDuration() {
return lastSentPacket;
}
public void setServerWasPaused() {
this.serverWasPaused = true;
}

View File

@@ -4,16 +4,22 @@ import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import com.replaymod.replaystudio.replay.ReplayFile;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreenWorking;
import net.minecraft.client.gui.GuiYesNo;
import net.minecraft.client.gui.GuiYesNoCallback;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.resources.I18n;
import net.minecraft.client.resources.ResourcePackRepository;
import net.minecraft.util.HttpUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if MC>=11300
import de.johni0702.minecraft.gui.utils.Consumer;
import net.minecraft.client.resources.DownloadingPackFinder;
//#else
//$$ import net.minecraft.client.gui.GuiScreenWorking;
//$$ import net.minecraft.client.resources.ResourcePackRepository;
//$$ import net.minecraft.util.HttpUtil;
//#endif
//#if MC>=10904
import net.minecraft.network.play.client.CPacketResourcePackStatus;
import net.minecraft.network.play.client.CPacketResourcePackStatus.Action;
@@ -33,11 +39,11 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.network.NetworkManager;
import org.apache.commons.io.FileUtils;
//#if MC<11300
//$$ import org.apache.commons.io.FileUtils;
//#endif
import javax.annotation.Nonnull;
import static com.replaymod.core.versions.MCVer.*;
//#else
//$$ import net.minecraft.client.multiplayer.ServerData.ServerResourceMode;
//$$ import net.minecraft.client.multiplayer.ServerList;
@@ -52,12 +58,14 @@ import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import static com.replaymod.core.versions.MCVer.*;
/**
* Records resource packs and handles incoming resource pack packets during recording.
*/
public class ResourcePackRecorder {
private static final Logger logger = LogManager.getLogger();
private static final Minecraft mc = Minecraft.getMinecraft();
private static final Minecraft mc = getMinecraft();
private final ReplayFile replayFile;
@@ -131,12 +139,12 @@ public class ResourcePackRecorder {
if (url.startsWith("level://")) {
String levelName = url.substring("level://".length());
File savesDir = new File(mc.mcDataDir, "saves");
File savesDir = new File(mcDataDir(mc), "saves");
final File levelDir = new File(savesDir, levelName);
if (levelDir.isFile()) {
netManager.sendPacket(makeStatusPacket(hash, Action.ACCEPTED));
Futures.addCallback(setServerResourcePack(mc.getResourcePackRepository(), levelDir), new FutureCallback<Object>() {
Futures.addCallback(setServerResourcePack(levelDir), new FutureCallback<Object>() {
@Override
public void onSuccess(Object result) {
recordResourcePack(levelDir, requestId);
@@ -163,7 +171,11 @@ public class ResourcePackRecorder {
//noinspection Convert2Lambda
mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback() {
@Override
public void confirmClicked(boolean result, int id) {
//#if MC>=11300
public void confirmResult(boolean result, int id) {
//#else
//$$ public void confirmClicked(boolean result, int id) {
//#endif
if (serverData != null) {
serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED);
}
@@ -202,100 +214,112 @@ public class ResourcePackRecorder {
});
}
//#if MC>=11300
private ListenableFuture downloadResourcePack(final int requestId, String url, String hash) {
final ResourcePackRepository repo = mc.mcResourcePackRepository;
String fileName;
if (hash.matches("^[a-f0-9]{40}$")) {
fileName = hash;
} else {
fileName = url.substring(url.lastIndexOf("/") + 1);
DownloadingPackFinder packFinder = mc.getPackFinder();
((IDownloadingPackFinder) packFinder).setRequestCallback(file -> recordResourcePack(file, requestId));
return packFinder.downloadResourcePack(url, hash);
}
if (fileName.contains("?")) {
fileName = fileName.substring(0, fileName.indexOf("?"));
}
if (!fileName.endsWith(".zip")) {
return Futures.immediateFailedFuture(new IllegalArgumentException("Invalid filename; must end in .zip"));
}
fileName = "legacy_" + fileName.replaceAll("\\W", "");
}
final File file = new File(repo.dirServerResourcepacks, fileName);
public interface IDownloadingPackFinder {
void setRequestCallback(Consumer<File> callback);
}
//#else
//$$ private ListenableFuture downloadResourcePack(final int requestId, String url, String hash) {
//$$ final ResourcePackRepository repo = mc.mcResourcePackRepository;
//$$ String fileName;
//$$ if (hash.matches("^[a-f0-9]{40}$")) {
//$$ fileName = hash;
//$$ } else {
//$$ fileName = url.substring(url.lastIndexOf("/") + 1);
//$$
//$$ if (fileName.contains("?")) {
//$$ fileName = fileName.substring(0, fileName.indexOf("?"));
//$$ }
//$$
//$$ if (!fileName.endsWith(".zip")) {
//$$ return Futures.immediateFailedFuture(new IllegalArgumentException("Invalid filename; must end in .zip"));
//$$ }
//$$
//$$ fileName = "legacy_" + fileName.replaceAll("\\W", "");
//$$ }
//$$
//$$ final File file = new File(repo.dirServerResourcepacks, fileName);
//#if MC>=10809
repo.lock.lock();
//$$ repo.lock.lock();
//#else
//$$ repo.field_177321_h.lock();
//#endif
try {
//$$ try {
//#if MC>=10809
repo.clearResourcePack();
//$$ repo.clearResourcePack();
//#else
//$$ repo.func_148529_f();
//#endif
if (file.exists() && hash.length() == 40) {
try {
String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
if (fileHash.equals(hash)) {
recordResourcePack(file, requestId);
return setServerResourcePack(repo, file);
}
logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it.");
FileUtils.deleteQuietly(file);
} catch (IOException ioexception) {
logger.warn("File " + file + " couldn\'t be hashed. Deleting it.", ioexception);
FileUtils.deleteQuietly(file);
}
}
final GuiScreenWorking guiScreen = new GuiScreenWorking();
final Minecraft mc = Minecraft.getMinecraft();
Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen)));
//$$
//$$ if (file.exists() && hash.length() == 40) {
//$$ try {
//$$ String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
//$$ if (fileHash.equals(hash)) {
//$$ recordResourcePack(file, requestId);
//$$ return setServerResourcePack(file);
//$$ }
//$$
//$$ logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it.");
//$$ FileUtils.deleteQuietly(file);
//$$ } catch (IOException ioexception) {
//$$ logger.warn("File " + file + " couldn\'t be hashed. Deleting it.", ioexception);
//$$ FileUtils.deleteQuietly(file);
//$$ }
//$$ }
//$$
//$$ final GuiScreenWorking guiScreen = new GuiScreenWorking();
//$$ final Minecraft mc = Minecraft.getMinecraft();
//$$
//$$ Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen)));
//$$
//#if MC>=10809
//#if MC>=11002
//#if MC>=11100
Map<String, String> sessionInfo = ResourcePackRepository.getDownloadHeaders();
//$$ Map<String, String> sessionInfo = ResourcePackRepository.getDownloadHeaders();
//#else
//$$ Map<String, String> sessionInfo = ResourcePackRepository.func_190115_a();
//#endif
//#else
//$$ Map<String, String> sessionInfo = Minecraft.getSessionInfo();
//#endif
repo.downloadingPacks = HttpUtil.downloadResourcePack(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
Futures.addCallback(repo.downloadingPacks, new FutureCallback<Object>() {
//$$ repo.downloadingPacks = HttpUtil.downloadResourcePack(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
//$$ Futures.addCallback(repo.downloadingPacks, new FutureCallback<Object>() {
//#else
//$$ Map sessionInfo = Minecraft.getSessionInfo();
//$$ repo.field_177322_i = HttpUtil.func_180192_a(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
//$$ Futures.addCallback(repo.field_177322_i, new FutureCallback() {
//#endif
@Override
public void onSuccess(Object value) {
recordResourcePack(file, requestId);
setServerResourcePack(repo, file);
}
@Override
public void onFailure(@Nonnull Throwable throwable) {
throwable.printStackTrace();
}
});
//$$ @Override
//$$ public void onSuccess(Object value) {
//$$ recordResourcePack(file, requestId);
//$$ setServerResourcePack(file);
//$$ }
//$$
//$$ @Override
//$$ public void onFailure(@Nonnull Throwable throwable) {
//$$ throwable.printStackTrace();
//$$ }
//$$ });
//#if MC>=10809
return repo.downloadingPacks;
//$$ return repo.downloadingPacks;
//#else
//$$ return repo.field_177322_i;
//#endif
} finally {
//$$ } finally {
//#if MC>=10809
repo.lock.unlock();
//$$ repo.lock.unlock();
//#else
//$$ repo.field_177321_h.unlock();
//#endif
}
}
//$$ }
//$$ }
//#endif
//#else
//$$ public synchronized S3FPacketCustomPayload handleResourcePack(S3FPacketCustomPayload packet) {
//$$ final int requestId = nextRequestId++;

View File

@@ -1,12 +1,29 @@
package com.replaymod.render;
import com.replaymod.core.versions.MCVer;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import net.minecraft.client.resources.I18n;
import org.lwjgl.util.ReadableColor;
import net.minecraft.util.Util;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import static com.replaymod.render.ReplayModRender.LOGGER;
//#if MC>=11300
import org.apache.maven.artifact.versioning.ComparableVersion;
//#else
//#if MC>=10800
//$$ import net.minecraftforge.fml.common.versioning.ComparableVersion;
//#else
//$$ import cpw.mods.fml.common.versioning.ComparableVersion;
//#endif
//#endif
@Data
public class RenderSettings {
@@ -21,6 +38,14 @@ public class RenderSettings {
public String getDescription() {
return I18n.format("replaymod.gui.rendersettings.renderer." + name().toLowerCase() + ".description");
}
public boolean isSpherical() {
return this == EQUIRECTANGULAR || this == ODS;
}
public boolean hasFixedAspectRatio() {
return this == EQUIRECTANGULAR || this == ODS || this == CUBIC;
}
}
public enum EncodingPreset {
@@ -49,7 +74,7 @@ public class RenderSettings {
}
public String getValue() {
return "-y -f rawvideo -pix_fmt rgb24 -s %WIDTH%x%HEIGHT% -r %FPS% -i - %FILTERS%" + preset;
return "-y -f rawvideo -pix_fmt bgra -s %WIDTH%x%HEIGHT% -r %FPS% -i - %FILTERS%" + preset;
}
public String getFileExtension() {
@@ -94,7 +119,9 @@ public class RenderSettings {
private final boolean stabilizePitch;
private final boolean stabilizeRoll;
private final ReadableColor chromaKeyingColor;
private final boolean inject360Metadata;
private final int sphericalFovX;
private final int sphericalFovY;
private final boolean injectSphericalMetadata;
private final AntiAliasing antiAliasing;
private final String exportCommand;
@@ -131,12 +158,66 @@ public class RenderSettings {
}
public String getVideoFilters() {
if (antiAliasing == AntiAliasing.NONE) {
return "";
} else {
StringBuilder filters = new StringBuilder();
if (antiAliasing != AntiAliasing.NONE) {
double factor = 1.0 / antiAliasing.getFactor();
return String.format("-vf scale=iw*%1$s:ih*%1$s ", factor);
filters.append(String.format("-filter:v scale=iw*%1$s:ih*%1$s ", factor));
}
return filters.toString();
}
public String getExportCommandOrDefault() {
return exportCommand.isEmpty() ? findFFmpeg() : exportCommand;
}
private static String findFFmpeg() {
switch (Util.getOSType()) {
case WINDOWS:
// Allow windows users to unpack the ffmpeg archive into a sub-folder of their .minecraft folder
File inDotMinecraft = new File(MCVer.mcDataDir(MCVer.getMinecraft()), "ffmpeg/bin/ffmpeg.exe");
if (inDotMinecraft.exists()) {
LOGGER.debug("FFmpeg found in .minecraft/ffmpeg");
return inDotMinecraft.getAbsolutePath();
}
break;
case OSX:
// The PATH doesn't seem to be set as expected on OSX, therefore we check some common locations ourselves
for (String path : new String[]{"/usr/local/bin/ffmpeg", "/usr/bin/ffmpeg"}) {
File file = new File(path);
if (file.exists()) {
LOGGER.debug("Found FFmpeg at {}", path);
return path;
} else {
LOGGER.debug("FFmpeg not located at {}", path);
}
}
// Homebrew doesn't seem to reliably symlink its installed binaries either
File homebrewFolder = new File("/usr/local/Cellar/ffmpeg");
String[] homebrewVersions = homebrewFolder.list();
if (homebrewVersions != null) {
Optional<File> latestOpt = Arrays.stream(homebrewVersions)
.map(ComparableVersion::new) // Convert file name to comparable version
.sorted(Comparator.reverseOrder()) // Sort for latest version
.map(ComparableVersion::toString) // Convert back to file name
.map(v -> new File(new File(homebrewFolder, v), "bin/ffmpeg")) // Convert to binary files
.filter(File::exists) // Filter invalid installations (missing executable)
.findFirst(); // Take first one
if (latestOpt.isPresent()) {
File latest = latestOpt.get();
LOGGER.debug("Found {} versions of FFmpeg installed with homebrew, chose {}",
homebrewVersions.length, latest);
return latest.getAbsolutePath();
}
}
break;
case LINUX: // Linux users are entrusted to have their PATH configured correctly (most package manager do this)
case SOLARIS: // Never heard of anyone running this mod on Solaris having any problems
case UNKNOWN: // Unknown OS, just try to use "ffmpeg"
}
LOGGER.debug("Using default FFmpeg executable");
return "ffmpeg";
}
}

View File

@@ -1,65 +1,57 @@
package com.replaymod.render;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod;
import com.replaymod.render.utils.RenderJob;
import com.replaymod.replay.events.ReplayCloseEvent;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraftforge.common.config.Configuration;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if MC>=10800
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import static com.replaymod.core.versions.MCVer.*;
@Mod(modid = ReplayModRender.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModRender {
public static final String MOD_ID = "replaymod-render";
@Mod.Instance(MOD_ID)
public class ReplayModRender implements Module {
{ instance = this; }
public static ReplayModRender instance;
private ReplayMod core;
public static Logger LOGGER;
public static Logger LOGGER = LogManager.getLogger();
private Configuration configuration;
private final List<RenderJob> renderQueue = new ArrayList<>();
public ReplayModRender(ReplayMod core) {
this.core = core;
core.getSettingsRegistry().register(Setting.class);
}
public ReplayMod getCore() {
return core;
}
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
LOGGER = event.getModLog();
core = ReplayMod.instance;
configuration = new Configuration(event.getSuggestedConfigurationFile());
@Override
public void initClient() {
FML_BUS.register(this);
core.getSettingsRegistry().register(Setting.class);
}
@SubscribeEvent
@@ -69,17 +61,17 @@ public class ReplayModRender {
public File getVideoFolder() {
String path = core.getSettingsRegistry().get(Setting.RENDER_PATH);
File folder = new File(path.startsWith("./") ? core.getMinecraft().mcDataDir : null, path);
File folder = new File(path.startsWith("./") ? mcDataDir(core.getMinecraft()) : null, path);
try {
FileUtils.forceMkdir(folder);
} catch (IOException e) {
throw new ReportedException(CrashReport.makeCrashReport(e, "Cannot create video folder."));
throw newReportedException(CrashReport.makeCrashReport(e, "Cannot create video folder."));
}
return folder;
}
public Configuration getConfiguration() {
return configuration;
public Path getRenderSettingsPath() {
return mcDataDir(core.getMinecraft()).toPath().resolve("config/replaymod-rendersettings.json");
}
public List<RenderJob> getRenderQueue() {

View File

@@ -1,19 +1,18 @@
package com.replaymod.render;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.frame.RGBFrame;
import com.replaymod.render.rendering.FrameConsumer;
import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.StreamPipe;
import net.minecraft.client.Minecraft;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.Util;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.TeeOutputStream;
import org.lwjgl.util.ReadableDimension;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -22,17 +21,8 @@ import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
//#if MC>=10800
import net.minecraftforge.fml.common.versioning.ComparableVersion;
//#else
//$$ import cpw.mods.fml.common.versioning.ComparableVersion;
//#endif
import static com.replaymod.render.ReplayModRender.LOGGER;
import static org.apache.commons.lang3.Validate.isTrue;
@@ -64,7 +54,7 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
.replace("%BITRATE%", String.valueOf(settings.getBitRate()))
.replace("%FILTERS%", settings.getVideoFilters());
String executable = settings.getExportCommand().isEmpty() ? findFFmpeg() : settings.getExportCommand();
String executable = settings.getExportCommandOrDefault();
LOGGER.info("Starting {} with args: {}", executable, commandArgs);
String[] cmdline;
try {
@@ -78,7 +68,7 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
} catch (IOException e) {
throw new NoFFmpegException(e);
}
File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "export.log");
File exportLogFile = new File(MCVer.mcDataDir(MCVer.getMinecraft()), "export.log");
OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream(exportLogFile), ffmpegLog);
new StreamPipe(process.getInputStream(), exportLogOut).start();
new StreamPipe(process.getErrorStream(), exportLogOut).start();
@@ -86,54 +76,6 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
channel = Channels.newChannel(outputStream);
}
private String findFFmpeg() {
switch (Util.getOSType()) {
case WINDOWS:
// Allow windows users to unpack the ffmpeg archive into a sub-folder of their .minecraft folder
File inDotMinecraft = new File(Minecraft.getMinecraft().mcDataDir, "ffmpeg/bin/ffmpeg.exe");
if (inDotMinecraft.exists()) {
LOGGER.debug("FFmpeg found in .minecraft/ffmpeg");
return inDotMinecraft.getAbsolutePath();
}
break;
case OSX:
// The PATH doesn't seem to be set as expected on OSX, therefore we check some common locations ourselves
for (String path : new String[]{"/usr/local/bin/ffmpeg", "/usr/bin/ffmpeg"}) {
File file = new File(path);
if (file.exists()) {
LOGGER.debug("Found FFmpeg at {}", path);
return path;
} else {
LOGGER.debug("FFmpeg not located at {}", path);
}
}
// Homebrew doesn't seem to reliably symlink its installed binaries either
File homebrewFolder = new File("/usr/local/Cellar/ffmpeg");
String[] homebrewVersions = homebrewFolder.list();
if (homebrewVersions != null) {
Optional<File> latestOpt = Arrays.stream(homebrewVersions)
.map(ComparableVersion::new) // Convert file name to comparable version
.sorted(Comparator.reverseOrder()) // Sort for latest version
.map(ComparableVersion::toString) // Convert back to file name
.map(v -> new File(new File(homebrewFolder, v), "bin/ffmpeg")) // Convert to binary files
.filter(File::exists) // Filter invalid installations (missing executable)
.findFirst(); // Take first one
if (latestOpt.isPresent()) {
File latest = latestOpt.get();
LOGGER.debug("Found {} versions of FFmpeg installed with homebrew, chose {}",
homebrewVersions.length, latest);
return latest.getAbsolutePath();
}
}
break;
case LINUX: // Linux users are entrusted to have their PATH configured correctly (most package manager do this)
case SOLARIS: // Never heard of anyone running this mod on Solaris having any problems
case UNKNOWN: // Unknown OS, just try to use "ffmpeg"
}
LOGGER.debug("Using default FFmpeg executable");
return "ffmpeg";
}
@Override
public void close() throws IOException {
IOUtils.closeQuietly(outputStream);
@@ -182,9 +124,9 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
}
CrashReport report = CrashReport.makeCrashReport(t, "Exporting frame");
CrashReportCategory exportDetails = report.makeCategory("Export details");
exportDetails.addCrashSection("Export command", settings.getExportCommand());
exportDetails.addCrashSection("Export args", commandArgs);
Minecraft.getMinecraft().crashed(report);
MCVer.addDetail(exportDetails, "Export command", settings::getExportCommand);
MCVer.addDetail(exportDetails, "Export args", commandArgs::toString);
MCVer.getMinecraft().crashed(report);
}
}

View File

@@ -5,10 +5,16 @@ import com.replaymod.render.blend.BlendMeshBuilder;
import com.replaymod.render.blend.Exporter;
import com.replaymod.render.blend.data.DMesh;
import com.replaymod.render.blend.data.DObject;
import net.minecraft.client.model.ModelBox;
import net.minecraft.client.model.ModelRenderer;
import org.lwjgl.opengl.GL11;
//#if MC>=11300
import net.minecraft.client.renderer.entity.model.ModelBox;
import net.minecraft.client.renderer.entity.model.ModelRenderer;
//#else
//$$ import net.minecraft.client.model.ModelBox;
//$$ import net.minecraft.client.model.ModelRenderer;
//#endif
import java.io.IOException;
import static com.replaymod.core.versions.MCVer.*;

View File

@@ -4,8 +4,8 @@ import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.rendering.Frame;
import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.PixelBufferObject;
import net.minecraft.client.renderer.OpenGlHelper;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -19,7 +19,7 @@ public abstract class MultiFramePboOpenGlFrameCapturer<F extends Frame, D extend
super(worldRenderer, renderInfo);
data = type.getEnumConstants();
int bufferSize = framePixels * 3 * data.length;
int bufferSize = framePixels * 4 * data.length;
pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
}
@@ -47,7 +47,7 @@ public abstract class MultiFramePboOpenGlFrameCapturer<F extends Frame, D extend
ByteBuffer pboBuffer = pbo.mapReadOnly();
OpenGlFrame[] frames = new OpenGlFrame[data.length];
int frameBufferSize = getFrameWidth() * getFrameHeight() * 3;
int frameBufferSize = getFrameWidth() * getFrameHeight() * 4;
for (int i = 0; i < frames.length; i++) {
ByteBuffer frameBuffer = ByteBufferPool.allocate(frameBufferSize);
pboBuffer.limit(pboBuffer.position() + frameBufferSize);
@@ -79,17 +79,10 @@ public abstract class MultiFramePboOpenGlFrameCapturer<F extends Frame, D extend
protected OpenGlFrame captureFrame(int frameId, D captureData) {
pbo.bind();
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
int offset = captureData.ordinal() * getFrameWidth() * getFrameHeight() * 3;
if (OpenGlHelper.isFramebufferEnabled()) {
frameBuffer().bindFramebufferTexture();
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, offset);
frameBuffer().unbindFramebufferTexture();
} else {
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, offset);
}
int offset = captureData.ordinal() * getFrameWidth() * getFrameHeight() * 4;
frameBuffer().bindFramebuffer(true);
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, offset);
frameBuffer().unbindFramebuffer();
pbo.unbind();
return null;

View File

@@ -6,14 +6,11 @@ import com.replaymod.render.frame.ODSOpenGlFrame;
import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.rendering.FrameCapturer;
import com.replaymod.render.shader.Program;
import net.minecraft.client.Minecraft;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.ReadableDimension;
//#if MC>=10800
import net.minecraft.client.renderer.GlStateManager;
import static net.minecraft.client.renderer.GlStateManager.*;
//#else
//$$ import com.replaymod.render.hooks.GLStateTracker.BooleanState;
@@ -36,9 +33,7 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
private final Program.Uniform leftEyeVariable;
private final BooleanState[] previousStates = new BooleanState[3];
private final BooleanState previousFogState;
private final Minecraft mc = Minecraft.getMinecraft();
private BooleanState previousFogState;
public ODSFrameCapturer(WorldRenderer worldRenderer, final RenderInfo renderInfo, int frameSize) {
RenderInfo fakeInfo = new RenderInfo() {
@@ -62,9 +57,9 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
@Override
public float updateForNextFrame() {
if (call++ % 2 == 0) {
shaderProgram.stopUsing();
unbindProgram();
partialTicks = renderInfo.updateForNextFrame();
shaderProgram.use();
bindProgram();
}
return partialTicks;
}
@@ -78,30 +73,43 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
right = new CubicStereoFrameCapturer(worldRenderer, fakeInfo, frameSize);
try {
shaderProgram = new Program(vertexResource, fragmentResource);
shaderProgram.use();
leftEyeVariable = shaderProgram.getUniformVariable("leftEye");
directionVariable = shaderProgram.getUniformVariable("direction");
setTexture("texture", 0);
setTexture("lightMap", 1);
linkState(0, "textureEnabled");
linkState(1, "lightMapEnabled");
linkState(2, "hurtTextureEnabled");
final Program.Uniform uniform = shaderProgram.getUniformVariable("fogEnabled");
previousFogState = fog();
uniform.set(previousFogState.currentState);
fog(new BooleanState(previousFogState.capability) {
@Override
public void setState(boolean state) {
super.setState(state);
uniform.set(state);
}
});
shaderProgram.stopUsing();
} catch (Exception e) {
throw new ReportedException(CrashReport.makeCrashReport(e, "Creating ODS shaders"));
throw newReportedException(CrashReport.makeCrashReport(e, "Creating ODS shaders"));
}
}
private void bindProgram() {
shaderProgram.use();
setTexture("texture", 0);
setTexture("lightMap", 1);
linkState(0, "textureEnabled");
linkState(1, "lightMapEnabled");
linkState(2, "hurtTextureEnabled");
final Program.Uniform uniform = shaderProgram.getUniformVariable("fogEnabled");
previousFogState = fog();
uniform.set(previousFogState.currentState);
fog(new BooleanState(previousFogState.capability) {
{ currentState = previousFogState.currentState; }
@Override
public void setState(boolean state) {
super.setState(state);
uniform.set(state);
}
});
}
private void unbindProgram() {
for (int i = 0; i < previousStates.length; i++) {
previousStates[i].currentState = texture2DState(i).currentState;
texture2DState(i, previousStates[i]);
}
previousFogState.currentState = fog().currentState;
fog(previousFogState);
shaderProgram.stopUsing();
}
private void setTexture(String texture, int i) {
shaderProgram.getUniformVariable(texture).set(i);
}
@@ -111,6 +119,7 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
previousStates[id] = texture2DState(id);
uniform.set(previousStates[id].currentState);
texture2DState(id, new BooleanState(previousStates[id].capability) {
{ currentState = previousStates[id].currentState; }
@Override
public void setState(boolean state) {
super.setState(state);
@@ -126,12 +135,12 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
@Override
public ODSOpenGlFrame process() {
shaderProgram.use();
bindProgram();
leftEyeVariable.set(true);
CubicOpenGlFrame leftFrame = left.process();
leftEyeVariable.set(false);
CubicOpenGlFrame rightFrame = right.process();
shaderProgram.stopUsing();
unbindProgram();
if (leftFrame != null && rightFrame != null) {
return new ODSOpenGlFrame(leftFrame, rightFrame);
@@ -144,10 +153,6 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
left.close();
right.close();
shaderProgram.delete();
for (int i = 0; i < previousStates.length; i++) {
texture2DState(i, previousStates[i]);
}
fog(previousFogState);
}
private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer {

View File

@@ -1,16 +1,17 @@
package com.replaymod.render.capturer;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.rendering.Frame;
import com.replaymod.render.rendering.FrameCapturer;
import com.replaymod.render.utils.ByteBufferPool;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import de.johni0702.minecraft.gui.utils.lwjgl.WritableDimension;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.shader.Framebuffer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import org.lwjgl.util.WritableDimension;
import org.lwjgl.opengl.GL12;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -30,7 +31,7 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
protected int framesDone;
private Framebuffer frameBuffer;
private final Minecraft mc = Minecraft.getMinecraft();
private final Minecraft mc = MCVer.getMinecraft();
public OpenGlFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo) {
this.worldRenderer = worldRenderer;
@@ -64,7 +65,7 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
protected Framebuffer frameBuffer() {
if (frameBuffer == null) {
frameBuffer = Minecraft.getMinecraft().getFramebuffer();
frameBuffer = mc.getFramebuffer();
}
return frameBuffer;
}
@@ -96,30 +97,28 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
}
protected OpenGlFrame captureFrame(int frameId, D captureData) {
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
ByteBuffer buffer = ByteBufferPool.allocate(getFrameWidth() * getFrameHeight() * 3);
if (OpenGlHelper.isFramebufferEnabled()) {
frameBuffer().bindFramebufferTexture();
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
frameBuffer().unbindFramebufferTexture();
} else {
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
}
ByteBuffer buffer = ByteBufferPool.allocate(getFrameWidth() * getFrameHeight() * 4);
frameBuffer().bindFramebuffer(true);
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, buffer);
frameBuffer().unbindFramebuffer();
buffer.rewind();
return new OpenGlFrame(frameId, new Dimension(getFrameWidth(), getFrameHeight()), buffer);
}
protected void resize(int width, int height) {
if (width != mc.displayWidth || height != mc.displayHeight) {
setWindowSize(width, height);
//#if MC>=11300
Framebuffer fb = mc.getFramebuffer();
if (fb.framebufferWidth != width || fb.framebufferHeight != height) {
fb.createFramebuffer(width, height);
}
}
private void setWindowSize(int width, int height) {
mc.resize(width, height);
mc.mainWindow.framebufferWidth = width;
mc.mainWindow.framebufferHeight = height;
//#else
//$$ if (width != mc.displayWidth || height != mc.displayHeight) {
//$$ mc.resize(width, height);
//$$ }
//#endif
}
@Override

View File

@@ -1,7 +1,7 @@
package com.replaymod.render.capturer;
import com.replaymod.render.RenderSettings;
import org.lwjgl.util.ReadableDimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
public interface RenderInfo {
ReadableDimension getFrameSize();

View File

@@ -3,9 +3,9 @@ package com.replaymod.render.capturer;
import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.PixelBufferObject;
import net.minecraft.client.renderer.OpenGlHelper;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.ReadableDimension;
import org.lwjgl.opengl.GL12;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -18,7 +18,7 @@ public class SimplePboOpenGlFrameCapturer extends OpenGlFrameCapturer<OpenGlFram
super(worldRenderer, renderInfo);
ReadableDimension size = renderInfo.getFrameSize();
bufferSize = size.getHeight() * size.getWidth() * 3;
bufferSize = size.getHeight() * size.getWidth() * 4;
pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
}
@@ -64,16 +64,9 @@ public class SimplePboOpenGlFrameCapturer extends OpenGlFrameCapturer<OpenGlFram
protected OpenGlFrame captureFrame(int frameId, CaptureData data) {
pbo.bind();
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
if (OpenGlHelper.isFramebufferEnabled()) {
frameBuffer().bindFramebufferTexture();
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, 0);
frameBuffer().unbindFramebufferTexture();
} else {
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, 0);
}
frameBuffer().bindFramebuffer(true);
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, 0);
frameBuffer().unbindFramebuffer();
pbo.unbind();
return null;

View File

@@ -5,7 +5,11 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor;
//#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.Event;
//#if MC>=11300
import net.minecraftforge.eventbus.api.Event;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.Event;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.Event;
//#endif

View File

@@ -1,9 +1,9 @@
package com.replaymod.render.frame;
import com.replaymod.render.rendering.Frame;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.lwjgl.util.ReadableDimension;
import java.nio.ByteBuffer;

View File

@@ -1,9 +1,9 @@
package com.replaymod.render.frame;
import com.replaymod.render.rendering.Frame;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import lombok.Getter;
import org.apache.commons.lang3.Validate;
import org.lwjgl.util.ReadableDimension;
import java.nio.ByteBuffer;
@@ -18,9 +18,9 @@ public class RGBFrame implements Frame {
private final ByteBuffer byteBuffer;
public RGBFrame(int frameId, ReadableDimension size, ByteBuffer byteBuffer) {
Validate.isTrue(size.getWidth() * size.getHeight() * 3 == byteBuffer.remaining(),
Validate.isTrue(size.getWidth() * size.getHeight() * 4 == byteBuffer.remaining(),
"Buffer size is %d (cap: %d) but should be %d",
byteBuffer.remaining(), byteBuffer.capacity(), size.getWidth() * size.getHeight() * 3);
byteBuffer.remaining(), byteBuffer.capacity(), size.getWidth() * size.getHeight() * 4);
this.frameId = frameId;
this.size = size;
this.byteBuffer = byteBuffer;

View File

@@ -13,11 +13,12 @@ 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.core.versions.MCVer.addDetail;
import static com.replaymod.core.versions.MCVer.newReportedException;
import static com.replaymod.render.ReplayModRender.LOGGER;
public class GuiExportFailed extends GuiScreen {
@@ -31,9 +32,9 @@ public class GuiExportFailed extends GuiScreen {
// 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);
addDetail(details, "Settings", settings::toString);
addDetail(details, "FFmpeg log", e::getLog);
throw newReportedException(crashReport);
} else {
// If they have, ask them whether it was intentional
GuiExportFailed gui = new GuiExportFailed(e, doRestart);
@@ -93,7 +94,9 @@ public class GuiExportFailed extends GuiScreen {
oldSettings.isStabilizePitch(),
oldSettings.isStabilizeRoll(),
oldSettings.getChromaKeyingColor(),
oldSettings.isInject360Metadata(),
oldSettings.getSphericalFovX(),
oldSettings.getSphericalFovY(),
oldSettings.isInjectSphericalMetadata(),
oldSettings.getAntiAliasing(),
oldSettings.getExportCommand(),
oldSettings.getEncodingPreset().getValue(),

View File

@@ -27,10 +27,10 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.crash.CrashReport;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
import java.util.List;

View File

@@ -1,5 +1,6 @@
package com.replaymod.render.gui;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.gson.Gson;
@@ -27,18 +28,22 @@ import de.johni0702.minecraft.gui.popup.GuiFileChooserPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer;
import de.johni0702.minecraft.gui.utils.Utils;
import de.johni0702.minecraft.gui.utils.lwjgl.Color;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.resources.I18n;
import net.minecraft.crash.CrashReport;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import org.lwjgl.util.Color;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableColor;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
@@ -128,7 +133,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
new GuiLabel().setI18nText("replaymod.gui.rendersettings.renderer"), renderMethodDropdown,
new GuiLabel().setI18nText("replaymod.gui.rendersettings.presets"), encodingPresetDropdown,
new GuiLabel().setI18nText("replaymod.gui.rendersettings.customresolution"), videoResolutionPanel,
new GuiLabel().setI18nText("replaymod.gui.settings.bitrate"), new GuiPanel().addElements(null,
new GuiLabel().setI18nText("replaymod.gui.rendersettings.bitrate"), new GuiPanel().addElements(null,
new GuiPanel().addElements(null, bitRateField, bitRateUnit).setLayout(new HorizontalLayout()),
frameRateSlider).setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(3)),
new GuiLabel().setI18nText("replaymod.gui.rendersettings.outputfile"), outputFileButton)
@@ -149,8 +154,22 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
.setI18nLabel("replaymod.gui.rendersettings.chromakey");
public final GuiColorPicker chromaKeyingColor = new GuiColorPicker().setSize(30, 15);
public final GuiCheckbox inject360Metadata = new GuiCheckbox()
.setI18nLabel("replaymod.gui.rendersettings.360metadata");
public static final int MIN_SPHERICAL_FOV = 120;
public static final int MAX_SPHERICAL_FOV = 360;
public static final int SPHERICAL_FOV_STEP_SIZE = 5;
public final GuiSlider sphericalFovSlider = new GuiSlider()
.onValueChanged(new Runnable() {
@Override
public void run() {
sphericalFovSlider.setText(I18n.format("replaymod.gui.rendersettings.sphericalFov")
+ ": " + (MIN_SPHERICAL_FOV + sphericalFovSlider.getValue() * SPHERICAL_FOV_STEP_SIZE) + "°");
updateInputs();
}
}).setSize(200, 20).setSteps((MAX_SPHERICAL_FOV - MIN_SPHERICAL_FOV) / SPHERICAL_FOV_STEP_SIZE);
public final GuiCheckbox injectSphericalMetadata = new GuiCheckbox()
.setI18nLabel("replaymod.gui.rendersettings.sphericalmetadata");
public final GuiDropdownMenu<RenderSettings.AntiAliasing> antiAliasingDropdown = new GuiDropdownMenu<RenderSettings.AntiAliasing>()
.setSize(200, 20).setValues(RenderSettings.AntiAliasing.values()).setSelected(RenderSettings.AntiAliasing.NONE);
@@ -161,8 +180,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
.addElements(new GridLayout.Data(0, 0.5),
new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel,
chromaKeyingCheckbox, chromaKeyingColor,
inject360Metadata,
new GuiLabel(), // to show the anti-aliasing options in a new line
injectSphericalMetadata, sphericalFovSlider,
new GuiLabel().setI18nText("replaymod.gui.rendersettings.antialiasing"), antiAliasingDropdown));
public final GuiTextField exportCommand = new GuiTextField().setI18nHint("replaymod.gui.rendersettings.command")
@@ -280,7 +298,13 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
this.replayHandler = replayHandler;
this.timeline = timeline;
String json = getConfigProperty(ReplayModRender.instance.getConfiguration()).getString();
String json = "{}";
try {
json = new String(Files.readAllBytes(getSettingsPath()), StandardCharsets.UTF_8);
} catch (NoSuchFileException | FileNotFoundException ignored) {
} catch (IOException e) {
LOGGER.error("Reading render settings:", e);
}
RenderSettings settings = new GsonBuilder()
.registerTypeAdapter(RenderSettings.class, (InstanceCreator<RenderSettings>) type -> getDefaultRenderSettings())
.registerTypeAdapter(ReadableColor.class, new Gson().getAdapter(Color.class))
@@ -289,8 +313,13 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
}
protected void updateInputs() {
RenderSettings.RenderMethod renderMethod = renderMethodDropdown.getSelectedValue();
// Enable/Disable video with input
videoWidth.setEnabled(!renderMethod.hasFixedAspectRatio());
// Validate video width and height
String error = isResolutionValid();
String error = updateResolution();
if (error == null) {
renderButton.setEnabled().setTooltip(null);
videoWidth.setTextColor(Colors.WHITE);
@@ -311,7 +340,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
}
// Enable/Disable camera stabilization checkboxes
switch (renderMethodDropdown.getSelectedValue()) {
switch (renderMethod) {
case CUBIC:
case EQUIRECTANGULAR:
case ODS:
@@ -321,52 +350,94 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
stabilizePanel.forEach(IGuiCheckbox.class).setDisabled();
}
// Enable/Disable Spherical FOV slider
sphericalFovSlider.setEnabled(renderMethod.isSpherical());
// Enable/Disable inject metadata checkbox
if (encodingPresetDropdown.getSelectedValue().getFileExtension().equals("mp4")
&& (renderMethodDropdown.getSelectedValue() == RenderSettings.RenderMethod.EQUIRECTANGULAR
|| renderMethodDropdown.getSelectedValue() == RenderSettings.RenderMethod.ODS)) {
inject360Metadata.setEnabled().setTooltip(null);
&& renderMethod.isSpherical()) {
injectSphericalMetadata.setEnabled().setTooltip(null);
} else {
inject360Metadata.setDisabled().setTooltip(new GuiTooltip().setColor(Colors.RED)
.setI18nText("replaymod.gui.rendersettings.360metadata.error"));
injectSphericalMetadata.setDisabled().setTooltip(new GuiTooltip().setColor(Colors.RED)
.setI18nText("replaymod.gui.rendersettings.sphericalmetadata.error"));
}
}
protected String isResolutionValid() {
protected String updateResolution() {
RenderSettings.EncodingPreset preset = encodingPresetDropdown.getSelectedValue();
RenderSettings.RenderMethod method = renderMethodDropdown.getSelectedValue();
int videoWidth = this.videoWidth.getInteger();
int videoHeight = this.videoHeight.getInteger();
int videoWidth;
if (method.hasFixedAspectRatio()) {
// cubic rendering requires an aspect ratio of 4:3,
// therefore the height must be divisible by 3
if (method == RenderSettings.RenderMethod.CUBIC && videoHeight % 3 != 0) {
return "replaymod.gui.rendersettings.customresolution.warning.cubic.height";
}
int sphericalFov = MIN_SPHERICAL_FOV + sphericalFovSlider.getValue() * SPHERICAL_FOV_STEP_SIZE;
videoWidth = videoWidthForHeight(method, videoHeight, sphericalFov, sphericalFov);
this.videoWidth.setValue(videoWidth);
} else {
videoWidth = this.videoWidth.getInteger();
}
// Make sure the export arguments haven't been changed manually
if (exportArguments.getText().equals(preset.getValue())) {
// Yuv420 requires both dimensions to be even
if (preset.isYuv420()
&& (videoWidth % 2 != 0 || videoHeight % 2 != 0)) {
if (method == RenderSettings.RenderMethod.CUBIC) {
// cubic yuv rendering has the special case that the height must be
// divisible by both 3 and 2 - tell the user about it with a special message
return "replaymod.gui.rendersettings.customresolution.warning.yuv420.cubic";
}
return "replaymod.gui.rendersettings.customresolution.warning.yuv420";
}
}
if (method == RenderSettings.RenderMethod.CUBIC
&& (videoWidth * 3 / 4 != videoHeight || videoWidth * 3 % 4 != 0)) {
return "replaymod.gui.rendersettings.customresolution.warning.cubic";
}
if (method == RenderSettings.RenderMethod.EQUIRECTANGULAR
&& (videoWidth / 2 != videoHeight || videoWidth % 2 != 0)) {
return "replaymod.gui.rendersettings.customresolution.warning.equirectangular";
}
if (method == RenderSettings.RenderMethod.ODS
&& videoWidth != videoHeight) {
return "replaymod.gui.rendersettings.customresolution.warning.ods";
}
//#if MC<10800
//$$ if (method == RenderSettings.RenderMethod.BLEND) {
//$$ return "replaymod.gui.rendersettings.no_blend_on_1_7_10";
//$$ }
//#endif
return null;
}
protected int videoWidthForHeight(RenderSettings.RenderMethod method, int height,
int sphericalFovX, int sphericalFovY) {
if (method.isSpherical()) {
if (sphericalFovY < 180) {
// calculate the non-cropped height of the video
height = Math.round(height * 180 / (float) sphericalFovY);
}
int width = height * 2;
if (sphericalFovX < 360) {
// crop the resulting width
width = Math.round(width * (float) sphericalFovX / 360);
}
if (method == RenderSettings.RenderMethod.ODS) {
width = Math.round(width / 2f);
}
return width;
} else if (method == RenderSettings.RenderMethod.CUBIC) {
Preconditions.checkArgument(height % 3 == 0);
return height / 3 * 4;
}
throw new IllegalArgumentException();
}
public void load(RenderSettings settings) {
renderMethodDropdown.setSelected(settings.getRenderMethod());
encodingPresetDropdown.setSelected(settings.getEncodingPreset());
@@ -402,7 +473,8 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
chromaKeyingCheckbox.setChecked(true);
chromaKeyingColor.setColor(settings.getChromaKeyingColor());
}
inject360Metadata.setChecked(settings.isInject360Metadata());
sphericalFovSlider.setValue((settings.getSphericalFovX() - MIN_SPHERICAL_FOV) / SPHERICAL_FOV_STEP_SIZE);
injectSphericalMetadata.setChecked(settings.isInjectSphericalMetadata());
antiAliasingDropdown.setSelected(settings.getAntiAliasing());
exportCommand.setText(settings.getExportCommand());
exportArguments.setText(settings.getExportArguments());
@@ -411,6 +483,8 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
}
public RenderSettings save(boolean serialize) {
int sphericalFov = MIN_SPHERICAL_FOV + sphericalFovSlider.getValue() * SPHERICAL_FOV_STEP_SIZE;
return new RenderSettings(
renderMethodDropdown.getSelectedValue(),
encodingPresetDropdown.getSelectedValue(),
@@ -424,7 +498,8 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
stabilizePitch.isChecked() && (serialize || stabilizePitch.isEnabled()),
stabilizeRoll.isChecked() && (serialize || stabilizeRoll.isEnabled()),
chromaKeyingCheckbox.isChecked() ? chromaKeyingColor.getColor() : null,
inject360Metadata.isChecked() && (serialize || inject360Metadata.isEnabled()),
sphericalFov, Math.min(180, sphericalFov),
injectSphericalMetadata.isChecked() && (serialize || injectSphericalMetadata.isEnabled()),
antiAliasingDropdown.getSelectedValue(),
exportCommand.getText(),
exportArguments.getText(),
@@ -438,23 +513,24 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
return new File(folder, fileName + "." + encodingPreset.getFileExtension());
}
protected Path getSettingsPath() {
return ReplayModRender.instance.getRenderSettingsPath();
}
private RenderSettings getDefaultRenderSettings() {
return new RenderSettings(RenderSettings.RenderMethod.DEFAULT, RenderSettings.EncodingPreset.MP4_DEFAULT, 1920, 1080, 60, 10 << 20, null,
true, false, false, false, null, false, RenderSettings.AntiAliasing.NONE, "", RenderSettings.EncodingPreset.MP4_DEFAULT.getValue(), false);
true, false, false, false, null, 360, 180, false, RenderSettings.AntiAliasing.NONE, "", RenderSettings.EncodingPreset.MP4_DEFAULT.getValue(), false);
}
@Override
public void close() {
RenderSettings settings = save(true);
String json = new Gson().toJson(settings);
Configuration config = ReplayModRender.instance.getConfiguration();
getConfigProperty(config).set(json);
config.save();
}
protected Property getConfigProperty(Configuration configuration) {
return configuration.get("rendersettings", "settings", "{}",
"Last state of the render settings GUI. Internal use only.");
try {
Files.write(getSettingsPath(), json.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
LOGGER.error("Saving render settings:", e);
}
}
public ReplayHandler getReplayHandler() {

View File

@@ -1,6 +1,7 @@
package com.replaymod.render.gui;
import com.replaymod.core.SettingsRegistry;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.ReplayModRender;
import com.replaymod.render.Setting;
@@ -13,13 +14,8 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import lombok.RequiredArgsConstructor;
import net.minecraft.util.Util;
import org.apache.logging.log4j.LogManager;
import org.lwjgl.Sys;
import java.awt.*;
import java.io.File;
import java.io.IOException;
@RequiredArgsConstructor
public class GuiRenderingDone extends GuiScreen {
@@ -34,30 +30,7 @@ public class GuiRenderingDone extends GuiScreen {
public final GuiButton openFolder = new GuiButton().onClick(new Runnable() {
@Override
public void run() {
File folder = videoFile.getParentFile();
String path = folder.getAbsolutePath();
// First try OS specific methods
try {
switch (Util.getOSType()) {
case WINDOWS:
Runtime.getRuntime().exec(String.format("cmd.exe /C start \"Open file\" \"%s\"", path));
return;
case OSX:
Runtime.getRuntime().exec(new String[]{"/usr/bin/open", path});
return;
}
} catch (IOException e) {
LogManager.getLogger().error("Cannot open file", e);
}
// Otherwise try the java way
try {
Desktop.getDesktop().browse(folder.toURI());
} catch (Throwable throwable) {
// And if all fails, lwjgl
Sys.openURL("file://" + path);
}
MCVer.openFile(videoFile.getParentFile());
}
}).setSize(200, 20).setI18nLabel("replaymod.gui.openfolder");

View File

@@ -11,18 +11,23 @@ import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiCheckbox;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
import de.johni0702.minecraft.gui.function.Tickable;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadablePoint;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import org.lwjgl.util.ReadablePoint;
//#if MC>=11300
import net.minecraft.client.renderer.texture.NativeImage;
//#endif
import java.nio.ByteBuffer;
public class GuiVideoRenderer extends GuiScreen {
public class GuiVideoRenderer extends GuiScreen implements Tickable {
private static final ResourceLocation NO_PREVIEW_TEXTURE = new ResourceLocation("replaymod", "logo.jpg");
private final VideoRenderer renderer;
@@ -135,7 +140,7 @@ public class GuiVideoRenderer extends GuiScreen {
private int currentIndex = 0;
@Override
public void draw(GuiRenderer guiRenderer, ReadableDimension size, RenderInfo renderInfo) {
public void tick() {
long current = System.currentTimeMillis();
//first, update the total render time (only if rendering is not paused and has already started)
@@ -206,8 +211,6 @@ public class GuiVideoRenderer extends GuiScreen {
int framesDone = renderer.getFramesDone(), framesTotal = renderer.getTotalFrames();
progressBar.setI18nLabel("replaymod.gui.rendering.progress", framesDone, framesTotal);
progressBar.setProgress((float) framesDone / framesTotal);
super.draw(guiRenderer, size, renderInfo);
}
private String secToString(int seconds) {
@@ -229,7 +232,11 @@ public class GuiVideoRenderer extends GuiScreen {
final int videoHeight = videoSize.getHeight();
if (previewTexture == null) {
previewTexture = new DynamicTexture(videoWidth, videoHeight);
//#if MC>=11300
previewTexture = new DynamicTexture(videoWidth, videoHeight, true);
//#else
//$$ previewTexture = new DynamicTexture(videoWidth, videoHeight);
//#endif
}
if (previewTextureDirty) {
@@ -263,13 +270,27 @@ public class GuiVideoRenderer extends GuiScreen {
ByteBuffer buffer = frame.getByteBuffer();
buffer.mark();
synchronized (this) {
int[] data = previewTexture.getTextureData();
// Optifine changes the texture data array to be three times as long (for use by shaders),
// we only want to initialize the first third which is why we use the length of the buffer instead
// of the length of the data array
for (int i = 0; buffer.remaining() > 0; i++) {
data[i] = 0xff << 24 | (buffer.get() & 0xff) << 16 | (buffer.get() & 0xff) << 8 | (buffer.get() & 0xff);
//#if MC>=11300
NativeImage data = previewTexture.getTextureData();
assert data != null;
for (int y = 0; y < data.getHeight(); y++) {
for (int x = 0; x < data.getWidth(); x++) {
int r = buffer.get() & 0xff;
int g = buffer.get() & 0xff;
int b = buffer.get() & 0xff;
int value = 0xff << 24 | b << 16 | g << 8 | r;
data.setPixelRGBA(x, y, value); // actually takes ABGR, not RGBA
}
}
//#else
//$$ int[] data = previewTexture.getTextureData();
//$$ // Optifine changes the texture data array to be three times as long (for use by shaders),
//$$ // we only want to initialize the first third which is why we use the length of the buffer instead
//$$ // of the length of the data array
//$$ for (int i = 0; buffer.remaining() > 0; i++) {
//$$ data[i] = 0xff << 24 | (buffer.get() & 0xff) << 16 | (buffer.get() & 0xff) << 8 | (buffer.get() & 0xff);
//$$ }
//#endif
previewTextureDirty = true;
}
buffer.reset();

View File

@@ -3,8 +3,6 @@ package com.replaymod.render.hooks;
import com.replaymod.render.utils.JailingQueue;
import net.minecraft.client.renderer.RegionRenderCacheBuilder;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.chunk.ChunkCompileTaskGenerator;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.client.renderer.chunk.ChunkRenderWorker;
import net.minecraft.client.renderer.chunk.RenderChunk;
@@ -12,6 +10,14 @@ import net.minecraft.client.renderer.chunk.RenderChunk;
import java.lang.reflect.Field;
import java.util.Iterator;
//#if MC>=11300
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.chunk.ChunkRenderTask;
//#else
//$$ import net.minecraft.client.renderer.RenderGlobal;
//$$ import net.minecraft.client.renderer.chunk.ChunkCompileTaskGenerator;
//#endif
//#if MC>=10904
import java.util.concurrent.PriorityBlockingQueue;
//#else
@@ -22,14 +28,28 @@ import static com.replaymod.core.versions.MCVer.*;
public class ChunkLoadingRenderGlobal {
private final RenderGlobal hooked;
//#if MC>=11300
private final WorldRenderer hooked;
//#else
//$$ private final RenderGlobal hooked;
//#endif
private ChunkRenderDispatcher renderDispatcher;
private JailingQueue<ChunkCompileTaskGenerator> workerJailingQueue;
//#if MC>=11300
private JailingQueue<ChunkRenderTask> workerJailingQueue;
//#else
//$$ private JailingQueue<ChunkCompileTaskGenerator> workerJailingQueue;
//#endif
private CustomChunkRenderWorker renderWorker;
private int frame;
@SuppressWarnings("unchecked")
public ChunkLoadingRenderGlobal(RenderGlobal renderGlobal) {
public ChunkLoadingRenderGlobal(
//#if MC>=11300
WorldRenderer renderGlobal
//#else
//$$ RenderGlobal renderGlobal
//#endif
) {
this.hooked = renderGlobal;
setup(renderGlobal.renderDispatcher);
@@ -51,14 +71,22 @@ public class ChunkLoadingRenderGlobal {
int workerThreads = renderDispatcher.listThreadedWorkers.size();
//#if MC>=10904
PriorityBlockingQueue<ChunkCompileTaskGenerator> queueChunkUpdates = renderDispatcher.queueChunkUpdates;
//#if MC>=11300
PriorityBlockingQueue<ChunkRenderTask> queueChunkUpdates = renderDispatcher.queueChunkUpdates;
//#else
//$$ PriorityBlockingQueue<ChunkCompileTaskGenerator> queueChunkUpdates = renderDispatcher.queueChunkUpdates;
//#endif
//#else
//$$ BlockingQueue<ChunkCompileTaskGenerator> queueChunkUpdates = renderDispatcher.queueChunkUpdates;
//#endif
workerJailingQueue = new JailingQueue<>(queueChunkUpdates);
renderDispatcher.queueChunkUpdates = workerJailingQueue;
//#if MC>=10904
ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null, 0);
//#if MC>=11300
ChunkRenderTask element = new ChunkRenderTask(null, null, 0);
//#else
//$$ ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null, 0);
//#endif
//#else
//$$ ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null);
//#endif
@@ -75,7 +103,11 @@ public class ChunkLoadingRenderGlobal {
renderDispatcher.queueChunkUpdates = queueChunkUpdates;
try {
Field hookField = RenderGlobal.class.getField("replayModRender_hook");
//#if MC>=11300
Field hookField = WorldRenderer.class.getField("replayModRender_hook");
//#else
//$$ Field hookField = RenderGlobal.class.getField("replayModRender_hook");
//#endif
hookField.set(hooked, this);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
@@ -119,7 +151,11 @@ public class ChunkLoadingRenderGlobal {
workerJailingQueue.freeAll();
try {
Field hookField = RenderGlobal.class.getField("replayModRender_hook");
//#if MC>=11300
Field hookField = WorldRenderer.class.getField("replayModRender_hook");
//#else
//$$ Field hookField = RenderGlobal.class.getField("replayModRender_hook");
//#endif
hookField.set(hooked, null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
@@ -139,7 +175,13 @@ public class ChunkLoadingRenderGlobal {
}
@Override
protected void processTask(ChunkCompileTaskGenerator p_178474_1_) throws InterruptedException {
protected void processTask(
//#if MC>=11300
ChunkRenderTask p_178474_1_
//#else
//$$ ChunkCompileTaskGenerator p_178474_1_
//#endif
) throws InterruptedException {
super.processTask(p_178474_1_);
}
}

View File

@@ -1,5 +1,6 @@
package com.replaymod.render.hooks;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.capturer.CaptureData;
import com.replaymod.render.capturer.RenderInfo;
@@ -8,8 +9,12 @@ import lombok.Getter;
import net.minecraft.client.Minecraft;
//#if MC>=10800
import net.minecraft.client.renderer.GlStateManager;
import net.minecraftforge.fml.common.FMLCommonHandler;
//#if MC>=11300
import net.minecraftforge.fml.hooks.BasicEventHooks;
//#else
//$$ import net.minecraft.client.renderer.GlStateManager;
//$$ import net.minecraftforge.fml.common.FMLCommonHandler;
//#endif
//#else
//$$ import com.replaymod.core.versions.MCVer.GlStateManager;
//$$ import cpw.mods.fml.common.FMLCommonHandler;
@@ -18,7 +23,7 @@ import net.minecraftforge.fml.common.FMLCommonHandler;
import java.io.IOException;
public class EntityRendererHandler implements WorldRenderer {
public final Minecraft mc = Minecraft.getMinecraft();
public final Minecraft mc = MCVer.getMinecraft();
@Getter
protected final RenderSettings settings;
@@ -44,21 +49,33 @@ public class EntityRendererHandler implements WorldRenderer {
}
public void renderWorld(float partialTicks, long finishTimeNano) {
FMLCommonHandler.instance().onRenderTickStart(partialTicks);
mc.entityRenderer.updateLightmap(partialTicks);
GlStateManager.enableDepth();
GlStateManager.enableAlpha();
GlStateManager.alphaFunc(516, 0.5F);
//#if MC>=11300
BasicEventHooks.onRenderTickStart(partialTicks);
//#else
//$$ FMLCommonHandler.instance().onRenderTickStart(partialTicks);
//#endif
//#if MC>=11300
mc.entityRenderer.renderWorld(partialTicks, finishTimeNano);
//#else
//$$ mc.entityRenderer.updateLightmap(partialTicks);
//$$
//$$ GlStateManager.enableDepth();
//$$ GlStateManager.enableAlpha();
//$$ GlStateManager.alphaFunc(516, 0.5F);
//$$
//#if MC>=10800
mc.entityRenderer.renderWorldPass(2, partialTicks, finishTimeNano);
//$$ mc.entityRenderer.renderWorldPass(2, partialTicks, finishTimeNano);
//#else
//$$ mc.entityRenderer.renderWorld(partialTicks, finishTimeNano);
//#endif
//#endif
FMLCommonHandler.instance().onRenderTickEnd(partialTicks);
//#if MC>=11300
BasicEventHooks.onRenderTickEnd(partialTicks);
//#else
//$$ FMLCommonHandler.instance().onRenderTickEnd(partialTicks);
//#endif
}
@Override
@@ -71,10 +88,6 @@ public class EntityRendererHandler implements WorldRenderer {
this.omnidirectional = omnidirectional;
}
public interface GluPerspective {
void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar);
}
public interface IEntityRenderer {
void replayModRender_setHandler(EntityRendererHandler handler);
EntityRendererHandler replayModRender_getHandler();

View File

@@ -4,6 +4,8 @@ import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.*;
import com.google.common.primitives.Bytes;
import com.googlecode.mp4parser.BasicContainer;
import com.replaymod.render.RenderSettings;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
@@ -30,12 +32,20 @@ public class MetadataInjector {
"<GSpherical:Stitched>true</GSpherical:Stitched> " +
"<GSpherical:StitchingSoftware>"+STITCHING_SOFTWARE+"</GSpherical:StitchingSoftware> " +
"<GSpherical:ProjectionType>equirectangular</GSpherical:ProjectionType> ";
private static final String SPHERICAL_CROP_XML =
"<GSpherical:FullPanoWidthPixels>%d</GSpherical:FullPanoWidthPixels> " +
"<GSpherical:FullPanoHeightPixels>%d</GSpherical:FullPanoHeightPixels> " +
"<GSpherical:CroppedAreaImageWidthPixels>%d</GSpherical:CroppedAreaImageWidthPixels> " +
"<GSpherical:CroppedAreaImageHeightPixels>%d</GSpherical:CroppedAreaImageHeightPixels> " +
"<GSpherical:CroppedAreaLeftPixels>%d</GSpherical:CroppedAreaLeftPixels> " +
"<GSpherical:CroppedAreaTopPixels>%d</GSpherical:CroppedAreaTopPixels> ";
private static final String STEREO_XML_CONTENTS = "<GSpherical:StereoMode>top-bottom</GSpherical:StereoMode>";
private static final String SPHERICAL_XML_FOOTER = "</rdf:SphericalVideo>";
private static final String XML_360_METADATA = SPHERICAL_XML_HEADER + SPHERICAL_XML_CONTENTS + SPHERICAL_XML_FOOTER;
private static final String XML_ODS_METADATA = SPHERICAL_XML_HEADER + SPHERICAL_XML_CONTENTS
+ STEREO_XML_CONTENTS + SPHERICAL_XML_FOOTER;
private static final String XML_MONO_METADATA = SPHERICAL_XML_HEADER + SPHERICAL_XML_CONTENTS
+ SPHERICAL_CROP_XML + SPHERICAL_XML_FOOTER;
private static final String XML_STEREO_METADATA = SPHERICAL_XML_HEADER + SPHERICAL_XML_CONTENTS
+ SPHERICAL_CROP_XML + STEREO_XML_CONTENTS + SPHERICAL_XML_FOOTER;
/**
* These bytes are taken from the variable 'spherical_uuid_id'
@@ -48,18 +58,43 @@ public class MetadataInjector {
(byte)0x02, (byte)0x52, (byte)0x1f, (byte)0xdd
};
private static final byte[] BYTES_360_METADATA = Bytes.concat(UUID_BYTES, XML_360_METADATA.getBytes());
private static final byte[] BYTES_ODS_METADATA = Bytes.concat(UUID_BYTES, XML_ODS_METADATA.getBytes());
public static void injectMetadata(RenderSettings.RenderMethod renderMethod, File videoFile,
int videoWidth, int videoHeight,
int sphericalFovX, int sphericalFovY) {
String xmlString;
switch (renderMethod) {
case EQUIRECTANGULAR:
xmlString = XML_MONO_METADATA;
break;
case ODS:
xmlString = XML_STEREO_METADATA;
break;
default:
throw new IllegalArgumentException("Invalid render method");
}
public static void inject360Metadata(File videoFile) {
writeMetadata(videoFile, BYTES_360_METADATA);
Dimension original = getOriginalDimensions(videoWidth, videoHeight, sphericalFovX, sphericalFovY);
writeMetadata(videoFile, String.format(xmlString,
original.getWidth(), original.getHeight(), videoWidth, videoHeight,
(original.getWidth() - videoWidth) / 2, (original.getHeight() - videoHeight) / 2));
}
public static void injectODSMetadata(File videoFile) {
writeMetadata(videoFile, BYTES_ODS_METADATA);
private static Dimension getOriginalDimensions(int videoWidth, int videoHeight,
int sphericalFovX, int sphericalFovY) {
if (sphericalFovX < 360) {
videoWidth = Math.round(videoWidth * 360 / (float) sphericalFovX);
}
if (sphericalFovY < 180) {
videoHeight = Math.round(videoHeight * 180 / (float) sphericalFovY);
}
return new Dimension(videoWidth, videoHeight);
}
private static void writeMetadata(File videoFile, byte[] metadata) {
private static void writeMetadata(File videoFile, String metadata) {
byte[] bytes = Bytes.concat(UUID_BYTES, metadata.getBytes());
File tempFile = null;
FileOutputStream videoFileOutputStream = null;
IsoFile tempIsoFile = null;
@@ -80,7 +115,7 @@ public class MetadataInjector {
//create a new UserBox, which actually contains the Metadata bytes
UserBox metadataBox = new UserBox(new byte[0]);
metadataBox.setData(metadata);
metadataBox.setData(bytes);
//add the Metadata UserBox to the Movie Track
trackBox.addBox(metadataBox);
@@ -97,7 +132,7 @@ public class MetadataInjector {
videoFileOutputStream = new FileOutputStream(videoFile);
tempIsoFile.getBox(videoFileOutputStream.getChannel());
} catch(Exception e) {
LOGGER.error("360 Degree Metadata couldn't be injected", e);
LOGGER.error("Spherical Metadata couldn't be injected", e);
} finally {
IOUtils.closeQuietly(tempIsoFile);
IOUtils.closeQuietly(videoFileOutputStream);

View File

@@ -1,20 +1,16 @@
package com.replaymod.render.mixin;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.capturer.CubicOpenGlFrameCapturer;
import com.replaymod.render.capturer.StereoscopicOpenGlFrameCapturer;
import com.replaymod.render.hooks.EntityRendererHandler;
import com.replaymod.replay.camera.CameraEntity;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.ReadableColor;
import org.lwjgl.util.glu.Project;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -22,6 +18,18 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11300
import net.minecraft.client.GameSettings;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.WorldRenderer;
//#else
//$$ import net.minecraft.client.renderer.EntityRenderer;
//$$ import net.minecraft.client.renderer.RenderGlobal;
//$$ import net.minecraft.client.settings.GameSettings;
//$$ import org.lwjgl.util.glu.Project;
//#endif
//#if MC>=10904
import net.minecraft.util.math.RayTraceResult;
//#else
@@ -31,13 +39,18 @@ import net.minecraft.util.math.RayTraceResult;
//#if MC>=10800
import net.minecraft.client.renderer.GlStateManager;
//#else
//$$ import net.minecraft.entity.EntityLivingBase;
//$$ import com.replaymod.core.versions.MCVer.GlStateManager;
//#endif
import static com.replaymod.core.versions.MCVer.*;
@Mixin(value = EntityRenderer.class)
public abstract class MixinEntityRenderer implements EntityRendererHandler.IEntityRenderer, EntityRendererHandler.GluPerspective {
//#if MC>=11300
@Mixin(value = GameRenderer.class)
//#else
//$$ @Mixin(value = EntityRenderer.class)
//#endif
public abstract class MixinEntityRenderer implements EntityRendererHandler.IEntityRenderer {
@Shadow
public Minecraft mc;
@@ -64,18 +77,21 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
//$$ }
//#endif
@Inject(method = "setupFog", at = @At("HEAD"), cancellable = true)
private void replayModRender_onSetupFog(int fogDistanceFlag, float partialTicks, CallbackInfo ci) {
if (replayModRender_handler == null) return;
if (replayModRender_handler.getSettings().getChromaKeyingColor() != null) {
ci.cancel();
}
}
// Moved to MixinFogRenderer in 1.13
//#if MC<11300
//$$ @Inject(method = "setupFog", at = @At("HEAD"), cancellable = true)
//$$ private void replayModRender_onSetupFog(int fogDistanceFlag, float partialTicks, CallbackInfo ci) {
//$$ if (replayModRender_handler == null) return;
//$$ if (replayModRender_handler.getSettings().getChromaKeyingColor() != null) {
//$$ ci.cancel();
//$$ }
//$$ }
//#endif
@Inject(method = "orientCamera", at = @At("HEAD"))
private void replayModRender_resetRotationIfNeeded(float partialTicks, CallbackInfo ci) {
if (replayModRender_handler != null) {
Entity entity = getRenderViewEntity(Minecraft.getMinecraft());
Entity entity = getRenderViewEntity(MCVer.getMinecraft());
RenderSettings settings = replayModRender_handler.getSettings();
if (settings.isStabilizeYaw()) {
entity.prevRotationYaw = entity.rotationYaw = 0;
@@ -96,9 +112,15 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
private float orgRoll;
@Inject(method = "setupCameraTransform", at = @At("HEAD"))
private void replayModRender_beforeSetupCameraTransform(float partialTicks, int renderPass, CallbackInfo ci) {
private void replayModRender_beforeSetupCameraTransform(
float partialTicks,
//#if MC<11300
//$$ int renderPass,
//#endif
CallbackInfo ci
) {
if (replayModRender_handler != null) {
Entity entity = getRenderViewEntity(Minecraft.getMinecraft());
Entity entity = getRenderViewEntity(MCVer.getMinecraft());
orgYaw = entity.rotationYaw;
orgPitch = entity.rotationPitch;
orgPrevYaw = entity.prevRotationYaw;
@@ -108,9 +130,15 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
}
@Inject(method = "setupCameraTransform", at = @At("RETURN"))
private void replayModRender_afterSetupCameraTransform(float partialTicks, int renderPass, CallbackInfo ci) {
private void replayModRender_afterSetupCameraTransform(
float partialTicks,
//#if MC<11300
//$$ int renderPass,
//#endif
CallbackInfo ci
) {
if (replayModRender_handler != null) {
Entity entity = getRenderViewEntity(Minecraft.getMinecraft());
Entity entity = getRenderViewEntity(MCVer.getMinecraft());
entity.rotationYaw = orgYaw;
entity.rotationPitch = orgPitch;
entity.prevRotationYaw = orgPrevYaw;
@@ -122,29 +150,46 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
}
@Inject(method = "renderHand", at = @At("HEAD"), cancellable = true)
private void replayModRender_renderSpectatorHand(float partialTicks, int renderPass, CallbackInfo ci) {
private void replayModRender_renderSpectatorHand(
float partialTicks,
//#if MC<11300
//$$ int renderPass,
//#endif
CallbackInfo ci
) {
if (replayModRender_handler != null) {
if (replayModRender_handler.omnidirectional) {
// No spectator hands during 360° view, we wouldn't even know where to put it
ci.cancel();
return; // No spectator hands during 360° view, we wouldn't even know where to put it
}
Entity currentEntity = getRenderViewEntity(Minecraft.getMinecraft());
if (currentEntity instanceof EntityPlayer && !(currentEntity instanceof CameraEntity)) {
if (renderPass == 2) { // Need to update render pass
renderPass = replayModRender_handler.data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE ? 1 : 0;
renderHand(partialTicks, renderPass);
ci.cancel();
} // else, render normally
//#if MC>=11300
}
//#else
//$$ } else {
//$$ Entity currentEntity = getRenderViewEntity(MCVer.getMinecraft());
//$$ if (currentEntity instanceof EntityPlayer && !(currentEntity instanceof CameraEntity)) {
//$$ if (renderPass == 2) { // Need to update render pass
//$$ renderPass = replayModRender_handler.data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE ? 1 : 0;
//$$ renderHand(partialTicks, renderPass);
//$$ ci.cancel();
//$$ } // else, render normally
//$$ }
//$$ }
//#endif
}
}
@Shadow
public abstract void renderHand(float partialTicks, int renderPass);
//#if MC<11300
//$$ @Shadow
//$$ public abstract void renderHand(float partialTicks, int renderPass);
//#endif
//#if MC>=11300
@Redirect(method = "updateCameraAndRender(FJ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/WorldRenderer;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/math/RayTraceResult;IF)V"))
private void replayModRender_drawSelectionBox(WorldRenderer instance, EntityPlayer player, RayTraceResult rtr, int alwaysZero, float partialTicks) {
//#else
//#if MC>=10904
@Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/math/RayTraceResult;IF)V"))
private void replayModRender_drawSelectionBox(RenderGlobal instance, EntityPlayer player, RayTraceResult rtr, int alwaysZero, float partialTicks) {
//$$ @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/math/RayTraceResult;IF)V"))
//$$ private void replayModRender_drawSelectionBox(RenderGlobal instance, EntityPlayer player, RayTraceResult rtr, int alwaysZero, float partialTicks) {
//#else
//#if MC>=10800
//$$ @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/MovingObjectPosition;IF)V"))
@@ -152,6 +197,7 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
//$$ @Redirect(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/MovingObjectPosition;IF)V"))
//#endif
//$$ private void replayModRender_drawSelectionBox(RenderGlobal instance, EntityPlayer player, MovingObjectPosition rtr, int alwaysZero, float partialTicks) {
//#endif
//#endif
if (replayModRender_handler == null) {
instance.drawSelectionBox(player, rtr, alwaysZero, partialTicks);
@@ -160,38 +206,51 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
private int orgRenderDistanceChunks;
//#if MC>=11300
@Inject(method = "updateCameraAndRender(FJ)V", at = @At(value = "JUMP", ordinal = 0))
//#else
//#if MC>=10800
@Inject(method = "renderWorldPass", at = @At(value = "JUMP", ordinal = 0))
//$$ @Inject(method = "renderWorldPass", at = @At(value = "JUMP", ordinal = 0))
//#else
//$$ @Inject(method = "renderWorld", at = @At(value = "INVOKE", ordinal = 0, shift = At.Shift.AFTER,
//$$ target = "Lnet/minecraft/client/renderer/culling/ClippingHelperImpl;getInstance()Lnet/minecraft/client/renderer/culling/ClippingHelper;"))
//#endif
//#endif
private void replayModRender_beforeRenderSky(CallbackInfo ci) {
if (replayModRender_handler != null && replayModRender_handler.getSettings().getChromaKeyingColor() != null) {
GameSettings settings = Minecraft.getMinecraft().gameSettings;
GameSettings settings = MCVer.getMinecraft().gameSettings;
orgRenderDistanceChunks = settings.renderDistanceChunks;
settings.renderDistanceChunks = 5; // Set render distance to 5 so we're always rendering sky when chroma keying
}
}
//#if MC>=11300
@Redirect(method = "updateCameraAndRender(FJ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/WorldRenderer;renderSky(F)V"))
private void replayModRender_renderSky(WorldRenderer instance, float partialTicks) {
//#else
//#if MC>=10800
@Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;renderSky(FI)V"))
private void replayModRender_renderSky(RenderGlobal instance, float partialTicks, int renderPass) {
//$$ @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;renderSky(FI)V"))
//$$ private void replayModRender_renderSky(RenderGlobal instance, float partialTicks, int renderPass) {
//#else
//$$ @Redirect(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;renderSky(F)V"))
//$$ private void replayModRender_renderSky(RenderGlobal instance, float partialTicks) {
//#endif
//#endif
if (replayModRender_handler != null && replayModRender_handler.getSettings().getChromaKeyingColor() != null) {
Minecraft.getMinecraft().gameSettings.renderDistanceChunks = orgRenderDistanceChunks;
MCVer.getMinecraft().gameSettings.renderDistanceChunks = orgRenderDistanceChunks;
ReadableColor color = replayModRender_handler.getSettings().getChromaKeyingColor();
GlStateManager.clearColor(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 1);
GlStateManager.clear(GL11.GL_COLOR_BUFFER_BIT);
} else {
//#if MC>=11300
instance.renderSky(partialTicks);
//#else
//#if MC>=10800
instance.renderSky(partialTicks, renderPass);
//$$ instance.renderSky(partialTicks, renderPass);
//#else
//$$ instance.renderSky(partialTicks);
//#endif
//#endif
}
}
@@ -204,12 +263,18 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
//#else
//$$ @Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glLoadIdentity()V", shift = At.Shift.AFTER, ordinal = 0, remap = false))
//#endif
private void replayModRender_setupStereoscopicProjection(float partialTicks, int renderPass, CallbackInfo ci) {
private void replayModRender_setupStereoscopicProjection(
float partialTicks,
//#if MC<11300
//$$ int renderPass,
//#endif
CallbackInfo ci
) {
if (replayModRender_getHandler() != null) {
if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) {
GlStateManager.translate(0.07, 0, 0);
GL11.glTranslatef(0.07f, 0, 0);
} else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) {
GlStateManager.translate(-0.07, 0, 0);
GL11.glTranslatef(-0.07f, 0, 0);
}
}
}
@@ -219,12 +284,18 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
//#else
//$$ @Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glLoadIdentity()V", shift = At.Shift.AFTER, ordinal = 1, remap = false))
//#endif
private void replayModRender_setupStereoscopicModelView(float partialTicks, int renderPass, CallbackInfo ci) {
private void replayModRender_setupStereoscopicModelView(
float partialTicks,
//#if MC<11300
//$$ int renderPass,
//#endif
CallbackInfo ci
) {
if (replayModRender_getHandler() != null) {
if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) {
GlStateManager.translate(0.1, 0, 0);
GL11.glTranslatef(0.1f, 0, 0);
} else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) {
GlStateManager.translate(-0.1, 0, 0);
GL11.glTranslatef(-0.1f, 0, 0);
}
}
}
@@ -233,63 +304,91 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
* Cubic Renderer
*/
@Redirect(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
private void replayModRender_gluPerspective$0(float fovY, float aspect, float zNear, float zFar) {
replayModRender_gluPerspective(fovY, aspect, zNear, zFar);
//#if MC>=11300
@Redirect(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Matrix4f;perspective(DFFF)Lnet/minecraft/client/renderer/Matrix4f;"))
private Matrix4f replayModRender_perspective$0(double fovY, float aspect, float zNear, float zFar) {
return replayModRender_perspective((float) fovY, aspect, zNear, zFar);
}
//#if MC>=10800
@Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
//#else
//$$ @Redirect(method = "renderHand", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
//#endif
private void replayModRender_gluPerspective$1(float fovY, float aspect, float zNear, float zFar) {
replayModRender_gluPerspective(fovY, aspect, zNear, zFar);
@Redirect(method = "updateCameraAndRender(FJ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Matrix4f;perspective(DFFF)Lnet/minecraft/client/renderer/Matrix4f;"))
private Matrix4f replayModRender_perspective$1(double fovY, float aspect, float zNear, float zFar) {
return replayModRender_perspective((float) fovY, aspect, zNear, zFar);
}
//#if MC>=10800
@Redirect(method = "renderCloudsCheck", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
private void replayModRender_gluPerspective$2(float fovY, float aspect, float zNear, float zFar) {
replayModRender_gluPerspective(fovY, aspect, zNear, zFar);
@Redirect(method = "renderCloudsCheck", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Matrix4f;perspective(DFFF)Lnet/minecraft/client/renderer/Matrix4f;"))
private Matrix4f replayModRender_perspective$2(double fovY, float aspect, float zNear, float zFar) {
return replayModRender_perspective((float) fovY, aspect, zNear, zFar);
}
//#endif
@Override
public void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar) {
private Matrix4f replayModRender_perspective(float fovY, float aspect, float zNear, float zFar) {
if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) {
fovY = 90;
aspect = 1;
}
Project.gluPerspective(fovY, aspect, zNear, zFar);
return Matrix4f.perspective(fovY, aspect, zNear, zFar);
}
//#else
//$$ @Redirect(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
//$$ private void replayModRender_gluPerspective$0(float fovY, float aspect, float zNear, float zFar) {
//$$ replayModRender_gluPerspective(fovY, aspect, zNear, zFar);
//$$ }
//$$
//#if MC>=10800
//$$ @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
//#else
//$$ @Redirect(method = "renderHand", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
//#endif
//$$ private void replayModRender_gluPerspective$1(float fovY, float aspect, float zNear, float zFar) {
//$$ replayModRender_gluPerspective(fovY, aspect, zNear, zFar);
//$$ }
//$$
//#if MC>=10800
//$$ @Redirect(method = "renderCloudsCheck", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
//$$ private void replayModRender_gluPerspective$2(float fovY, float aspect, float zNear, float zFar) {
//$$ replayModRender_gluPerspective(fovY, aspect, zNear, zFar);
//$$ }
//#endif
//$$
//$$ private void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar) {
//$$ if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) {
//$$ fovY = 90;
//$$ aspect = 1;
//$$ }
//$$ Project.gluPerspective(fovY, aspect, zNear, zFar);
//$$ }
//#endif
@Inject(method = "orientCamera", at = @At("HEAD"))
private void replayModRender_setupCubicFrameRotation(float partialTicks, CallbackInfo ci) {
if (replayModRender_getHandler() != null && replayModRender_getHandler().data instanceof CubicOpenGlFrameCapturer.Data) {
switch ((CubicOpenGlFrameCapturer.Data) replayModRender_getHandler().data) {
case FRONT:
GlStateManager.rotate(0, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(0, 0.0F, 1.0F, 0.0F);
break;
case RIGHT:
GlStateManager.rotate(90, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(90, 0.0F, 1.0F, 0.0F);
break;
case BACK:
GlStateManager.rotate(180, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(180, 0.0F, 1.0F, 0.0F);
break;
case LEFT:
GlStateManager.rotate(-90, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(-90, 0.0F, 1.0F, 0.0F);
break;
case BOTTOM:
GlStateManager.rotate(90, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(90, 1.0F, 0.0F, 0.0F);
break;
case TOP:
GlStateManager.rotate(-90, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(-90, 1.0F, 0.0F, 0.0F);
break;
}
}
if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) {
// Minecraft goes back a little so we have to revert that
GlStateManager.translate(0.0F, 0.0F, 0.1F);
//#if MC>=11300
GL11.glTranslatef(0.0F, 0.0F, -0.05F);
//#else
//$$ GL11.glTranslatef(0.0F, 0.0F, 0.1F);
//#endif
}
}
}

View File

@@ -0,0 +1,24 @@
//#if MC>=11300
package com.replaymod.render.mixin;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.renderer.FogRenderer;
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.CallbackInfo;
@Mixin(FogRenderer.class)
public abstract class MixinFogRenderer {
@Inject(method = "setupFog", at = @At("HEAD"), cancellable = true)
private void replayModRender_onSetupFog(int fogDistanceFlag, float partialTicks, CallbackInfo ci) {
EntityRendererHandler handler =
((EntityRendererHandler.IEntityRenderer) MCVer.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler == null) return;
if (handler.getSettings().getChromaKeyingColor() != null) {
ci.cancel();
}
}
}
//#endif

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