Split off highly version dependent code from ReplayMod

This commit is contained in:
Jonas Herzig
2021-02-20 00:41:40 +01:00
parent bc9d8cb3d1
commit 0be2082fe7
12 changed files with 443 additions and 401 deletions

View File

@@ -5,8 +5,9 @@ import com.replaymod.compat.ReplayModCompat;
import com.replaymod.core.gui.GuiBackgroundProcesses;
import com.replaymod.core.gui.GuiReplaySettings;
import com.replaymod.core.gui.RestoreReplayGui;
import com.replaymod.core.mixin.MinecraftAccessor;
import com.replaymod.core.versions.MCVer;
import com.replaymod.core.versions.scheduler.Scheduler;
import com.replaymod.core.versions.scheduler.SchedulerImpl;
import com.replaymod.editor.ReplayModEditor;
import com.replaymod.extras.ReplayModExtras;
import com.replaymod.recording.ReplayModRecording;
@@ -17,7 +18,6 @@ import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.us.myles.ViaVersion.api.protocol.ProtocolVersion;
import com.replaymod.replaystudio.util.I18n;
import com.replaymod.replaystudio.viaversion.ViaVersionPacketConverter;
import com.replaymod.simplepathing.ReplayModSimplePathing;
import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraft.client.MinecraftClient;
@@ -28,63 +28,13 @@ import net.minecraft.text.Style;
import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting;
import net.minecraft.util.crash.CrashException;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.FileUtils;
//#if MC>=11400
import net.minecraft.client.options.Option;
import net.minecraft.util.thread.ReentrantThreadExecutor;
//#endif
//#if FABRIC>=1
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.loader.api.FabricLoader;
//#else
//$$ import com.google.common.util.concurrent.ListenableFutureTask;
//$$ import net.minecraftforge.eventbus.api.SubscribeEvent;
//$$ import java.util.Queue;
//$$ import java.util.concurrent.FutureTask;
//$$
//#if MC>=11400
//$$ import com.replaymod.core.versions.LangResourcePack;
//$$ 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 static com.replaymod.core.versions.MCVer.FML_BUS;
//$$ import net.minecraft.client.resources.IResourcePack;
//$$ import net.minecraftforge.common.config.Configuration;
//#endif
//$$
//#if MC>=10800
//$$ import net.minecraft.client.GameSettings;
//#endif
//$$
//#if MC>=11400
//$$ import net.minecraftforge.fml.ModList;
//#else
//$$ 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.event.FMLInitializationEvent;
//$$ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
//#if MC>=10800
//$$ import net.minecraftforge.fml.client.FMLClientHandler;
//#else
//$$ import com.replaymod.replay.InputReplayTimer;
//$$
//$$ import java.util.ArrayDeque;
//#endif
//#endif
//$$ import net.minecraftforge.fml.common.Mod;
//#if MC<11400
//$$ import net.minecraftforge.fml.common.gameevent.TickEvent;
//#endif
//$$ import net.minecraft.client.settings.GameSettings;
//#endif
import java.io.ByteArrayInputStream;
@@ -102,43 +52,9 @@ import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
//#if FABRIC<=0
//#if MC>=11400
//$$ @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",
//#endif
//$$ guiFactory = "com.replaymod.core.gui.GuiFactory")
//#endif
//#endif
public class ReplayMod implements
//#if FABRIC>=1
ClientModInitializer,
//#endif
Module
{
public static String getMinecraftVersion() {
//#if MC>=11400
return MinecraftClient.getInstance().getGame().getVersion().getName();
//#else
//#if MC>=11400
//$$ return MCPVersion.getMCVersion();
//#else
//$$ return Loader.MC_VERSION;
//#endif
//#endif
}
public class ReplayMod implements Module, Scheduler {
public static final String MOD_ID = "replaymod";
@@ -148,23 +64,15 @@ public class ReplayMod implements
private static final MinecraftClient mc = MCVer.getMinecraft();
//#if MC<11400
//$$ @Deprecated
//$$ public static Configuration config;
//#endif
private final ReplayModBackend backend;
private final SchedulerImpl scheduler = new SchedulerImpl();
private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry();
private final SettingsRegistry settingsRegistry = new SettingsRegistry();
{
settingsRegistry.register(Setting.class);
}
// The instance of your mod that Forge uses.
//#if MC>=11400
{ instance = this; }
//#else
//$$ @Instance(MOD_ID)
//#endif
public static ReplayMod instance;
private final List<Module> modules = new ArrayList<>();
@@ -181,15 +89,15 @@ public class ReplayMod implements
*/
private boolean minimalMode;
public ReplayMod() {
public ReplayMod(ReplayModBackend backend) {
this.backend = backend;
I18n.setI18n(net.minecraft.client.resource.language.I18n::translate);
//#if MC>=11400
// Check Minecraft protocol version for compatibility
if (!ProtocolVersion.isRegistered(MCVer.getProtocolVersion()) && !Boolean.parseBoolean(System.getProperty("replaymod.skipversioncheck", "false"))) {
minimalMode = true;
}
//#endif
// Register all RM modules
modules.add(this);
@@ -205,16 +113,6 @@ public class ReplayMod implements
settingsRegistry.register();
}
//#if MC<=11400
//$$ @EventHandler
//$$ public void init(FMLPreInitializationEvent event) {
//$$ config = new Configuration(event.getSuggestedConfigurationFile());
//$$ config.load();
//$$ settingsRegistry.backend.setConfiguration(config);
//$$ settingsRegistry.save(); // Save default values to disk
//$$ }
//#endif
public KeyBindingRegistry getKeyBindingRegistry() {
return keyBindingRegistry;
}
@@ -273,19 +171,14 @@ public class ReplayMod implements
return replayFolder.resolve(relative);
}
public static final DirectoryResourcePack jGuiResourcePack;
public static final DirectoryResourcePack jGuiResourcePack = createJGuiResourcePack();
public static final String JGUI_RESOURCE_PACK_NAME = "replaymod_jgui";
static { // Note: even preInit is too late and we'd have to issue another resource reload
jGuiResourcePack = initJGuiResourcePack();
}
private static DirectoryResourcePack initJGuiResourcePack() {
private static DirectoryResourcePack createJGuiResourcePack() {
File folder = new File("../jGui/src/main/resources");
if (!folder.exists()) {
return null;
}
//noinspection UnnecessaryLocalVariable
DirectoryResourcePack jGuiResourcePack = new DirectoryResourcePack(folder) {
return new DirectoryResourcePack(folder) {
@Override
//#if MC>=11400
public String getName() {
@@ -313,56 +206,13 @@ public class ReplayMod implements
}
}
};
//#if MC<=11202
//$$ @SuppressWarnings("unchecked")
//$$ List<IResourcePack> defaultResourcePacks = ((MinecraftAccessor) mc).getDefaultResourcePacks();
//$$ defaultResourcePacks.add(jGuiResourcePack);
//#endif
//#if MC<=10710
//$$ FolderResourcePack mainResourcePack = new FolderResourcePack(new File("../src/main/resources")) {
//$$ @Override
//$$ protected InputStream getInputStreamByName(String resourceName) throws IOException {
//$$ try {
//$$ return super.getInputStreamByName(resourceName);
//$$ } catch (IOException e) {
//$$ if ("pack.mcmeta".equals(resourceName)) {
//$$ return new ByteArrayInputStream(("{\"pack\": {\"description\": \"dummy pack for mod resources in dev-env\", \"pack_format\": 1}}").getBytes(StandardCharsets.UTF_8));
//$$ }
//$$ throw e;
//$$ }
//$$ }
//$$ };
//$$ defaultResourcePacks.add(mainResourcePack);
//#endif
return jGuiResourcePack;
}
//#if FABRIC>=1
@Override
public void onInitializeClient() {
void initModules() {
modules.forEach(Module::initCommon);
modules.forEach(Module::initClient);
modules.forEach(m -> m.registerKeyBindings(keyBindingRegistry));
}
//#else
//#if MC>=11400
//$$ {
//$$ 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
//#endif
@Override
public void registerKeyBindings(KeyBindingRegistry registry) {
@@ -373,10 +223,6 @@ public class ReplayMod implements
@Override
public void initClient() {
//#if MC<=10710
//$$ FML_BUS.register(this); // For runLater(Runnable)
//#endif
backgroundProcesses.register();
keyBindingRegistry.register();
@@ -391,8 +237,6 @@ public class ReplayMod implements
}
//#endif
testIfMoeshAndExitMinecraft();
runPostStartup(() -> {
final long DAYS = 24 * 60 * 60 * 1000;
@@ -491,231 +335,33 @@ public class ReplayMod implements
});
}
/**
* Execute the given runnable on the main client thread, returning only after it has been run (or after 30 seconds).
*/
@Override
public void runSync(Runnable runnable) throws InterruptedException, ExecutionException, TimeoutException {
//#if MC>=11400
if (mc.isOnThread()) {
runnable.run();
} else {
executor.submit(() -> {
runnable.run();
return null;
}).get(30, TimeUnit.SECONDS);
}
//#else
//$$ if (mc.isCallingFromMinecraftThread()) {
//$$ runnable.run();
//$$ } else {
//$$ FutureTask<Void> future = new FutureTask<>(runnable, null);
//$$ runLater(future);
//$$ future.get(30, TimeUnit.SECONDS);
//$$ }
//#endif
scheduler.runSync(runnable);
}
/**
* Execute the given runnable after game has started (once the overlay has been closed).
* Most importantly, it will run after resources (including language keys!) have been loaded.
* Below 1.14, this is equivalent to {@link #runLater(Runnable)}.
*/
@Override
public void runPostStartup(Runnable runnable) {
runLater(new Runnable() {
@Override
public void run() {
//#if MC>=11400
if (getMinecraft().overlay != null) {
// delay until after resources have been loaded
runLater(this);
return;
}
//#endif
runnable.run();
}
});
scheduler.runPostStartup(runnable);
}
/**
* Set when the currently running code has been scheduled by runLater.
* If this is the case, subsequent calls to runLater have to be delayed until all scheduled tasks have been
* processed, otherwise a livelock may occur.
*/
private boolean inRunLater = false;
//#if MC>=11400
private boolean inRenderTaskQueue = false;
// Starting 1.14 MC clears the queue of scheduled tasks on disconnect.
// This works fine for MC since it uses the queue only for packet handling but breaks our assumption that
// stuff submitted via runLater is actually always run (e.g. recording might not be fully stopped because parts
// of that are run via runLater and stopping the recording happens right around the time MC clears the queue).
// Luckily, that's also the version where MC pulled out the executor implementation, so we can just spin up our own.
public static class ReplayModExecutor extends ReentrantThreadExecutor<Runnable> {
private final Thread mcThread = Thread.currentThread();
// Fail-fast in case we ever switch to async loading and forget to change this
// (except for fabric 1.15+ because it loads the mod before the client thread is set)
//#if FABRIC<1 || MC<11500
//$$ { if (!MinecraftClient.getInstance().isOnThread()) throw new RuntimeException(); }
//#endif
private ReplayModExecutor(String string_1) {
super(string_1);
}
@Override
protected Runnable createTask(Runnable runnable) {
return runnable;
}
@Override
protected boolean canExecute(Runnable runnable) {
return true;
}
@Override
protected Thread getThread() {
return mcThread;
}
@Override
public void runTasks() {
super.runTasks();
}
}
public final ReplayModExecutor executor = new ReplayModExecutor("Client/ReplayMod");
//#endif
// Pre-1.14 MC would hold the lock on the scheduledTasks queue while executing its tasks
// such that no new tasks could be submitted while one of them was running.
// This would cause issues with long-running tasks (e.g. video rendering) as it would
// block all async tasks (e.g. skin loading).
@Override
public void runLaterWithoutLock(Runnable runnable) {
//#if MC>=11400
// MC 1.14+ no longer synchronizes on the queue while running its tasks
runLater(runnable);
//#else
//$$ runLater(() -> runLaterWithoutLock(runnable), runnable);
//#endif
scheduler.runLaterWithoutLock(runnable);
}
@Override
public void runLater(Runnable runnable) {
runLater(runnable, () -> runLater(runnable));
scheduler.runLater(runnable);
}
private void runLater(Runnable runnable, Runnable defer) {
//#if MC>=11400
if (mc.isOnThread() && inRunLater && !inRenderTaskQueue) {
((MinecraftAccessor) mc).getRenderTaskQueue().offer(() -> {
inRenderTaskQueue = true;
try {
defer.run();
} finally {
inRenderTaskQueue = false;
}
});
} else {
executor.send(() -> {
inRunLater = true;
try {
runnable.run();
} catch (CrashException e) {
e.printStackTrace();
System.err.println(e.getReport().asString());
mc.setCrashReport(e.getReport());
} finally {
inRunLater = false;
}
});
}
//#else
//$$ if (mc.isCallingFromMinecraftThread() && inRunLater) {
//#if MC>=10800
//$$ FML_BUS.register(new Object() {
//$$ @SubscribeEvent
//$$ public void onRenderTick(TickEvent.RenderTickEvent event) {
//$$ if (event.phase == TickEvent.Phase.START) {
//$$ FML_BUS.unregister(this);
//$$ defer.run();
//$$ }
//$$ }
//$$ });
//#else
//$$ FML_BUS.register(new RunLaterHelper(defer));
//#endif
//$$ return;
//$$ }
//#if MC>=10800
//$$ Queue<FutureTask<?>> tasks = ((MinecraftAccessor) mc).getScheduledTasks();
//$$ //noinspection SynchronizationOnLocalVariableOrMethodParameter
//$$ synchronized (tasks) {
//#else
//$$ Queue<ListenableFutureTask<?>> tasks = scheduledTasks;
//$$ synchronized (scheduledTasks) {
//#endif
//$$ tasks.add(ListenableFutureTask.create(() -> {
//$$ inRunLater = true;
//$$ try {
//$$ runnable.run();
//$$ } catch (ReportedException e) {
//$$ e.printStackTrace();
//$$ System.err.println(e.getCrashReport().getCompleteReport());
//$$ mc.crashed(e.getCrashReport());
//$$ } finally {
//$$ inRunLater = false;
//$$ }
//$$ }, null));
//$$ }
//#endif
@Override
public void runTasks() {
scheduler.runTasks();
}
//#if MC<=10710
//$$ // 1.7.10: Cannot use MC's because it is processed only during ticks (so not at all when replay is paused)
//$$ private final Queue<ListenableFutureTask<?>> scheduledTasks = new ArrayDeque<>();
//$$
//$$ // in 1.7.10 apparently events can't be delivered to anonymous classes
//$$ public class RunLaterHelper {
//$$ private final Runnable defer;
//$$
//$$ private RunLaterHelper(Runnable defer) {
//$$ this.defer = defer;
//$$ }
//$$
//$$ @SubscribeEvent
//$$ public void onRenderTick(TickEvent.RenderTickEvent event) {
//$$ if (event.phase == TickEvent.Phase.START) {
//$$ FML_BUS.unregister(this);
//$$ defer.run();
//$$ }
//$$ }
//$$ }
//$$
//$$ @SubscribeEvent
//$$ public void runScheduledTasks(InputReplayTimer.RunScheduledTasks event) {
//$$ synchronized (scheduledTasks) {
//$$ while (!scheduledTasks.isEmpty()) {
//$$ scheduledTasks.poll().run();
//$$ }
//$$ }
//$$ }
//#endif
public String getVersion() {
//#if FABRIC>=1
return FabricLoader.getInstance().getModContainer(MOD_ID)
.orElseThrow(IllegalStateException::new)
.getMetadata().getVersion().toString();
//#else
//#if MC>=11400
//$$ return ModList.get().getModContainerById(MOD_ID).get().getModInfo().getVersion().toString();
//#else
//$$ return Loader.instance().getIndexedModList().get(MOD_ID).getVersion();
//#endif
//#endif
}
private void testIfMoeshAndExitMinecraft() {
if("currentPlayer".equals("Moesh")) {
System.exit(-1);
}
return backend.getVersion();
}
public MinecraftClient getMinecraft() {

View File

@@ -0,0 +1,21 @@
package com.replaymod.core;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.loader.api.FabricLoader;
import static com.replaymod.core.ReplayMod.MOD_ID;
public class ReplayModBackend implements ClientModInitializer {
private final ReplayMod mod = new ReplayMod(this);
@Override
public void onInitializeClient() {
mod.initModules();
}
public String getVersion() {
return FabricLoader.getInstance().getModContainer(MOD_ID)
.orElseThrow(IllegalStateException::new)
.getMetadata().getVersion().toString();
}
}

View File

@@ -176,6 +176,14 @@ public class MCVer {
);
}
public static String getMinecraftVersion() {
//#if MC>=11400
return getMinecraft().getGame().getVersion().getName();
//#else
//$$ return Loader.MC_VERSION;
//#endif
}
public static void addDetail(CrashReportSection category, String name, Callable<String> callable) {
//#if MC>=10904
//#if MC>=11200

View File

@@ -0,0 +1,30 @@
package com.replaymod.core.versions.scheduler;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
public interface Scheduler {
/**
* Execute the given runnable on the main client thread, returning only after it has been run (or after 30 seconds).
*/
void runSync(Runnable runnable) throws InterruptedException, ExecutionException, TimeoutException;
/**
* Execute the given runnable after game has started (once the overlay has been closed).
* Most importantly, it will run after resources (including language keys!) have been loaded.
* Below 1.14, this is equivalent to {@link #runLater(Runnable)}.
*/
void runPostStartup(Runnable runnable);
/**
* Pre-1.14 MC would hold the lock on the scheduledTasks queue while executing its tasks
* such that no new tasks could be submitted while one of them was running.
* This would cause issues with long-running tasks (e.g. video rendering) as it would
* block all async tasks (e.g. skin loading).
*/
void runLaterWithoutLock(Runnable runnable);
void runLater(Runnable runnable);
void runTasks();
}

View File

@@ -0,0 +1,124 @@
package com.replaymod.core.versions.scheduler;
import com.replaymod.core.mixin.MinecraftAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.crash.CrashException;
import net.minecraft.util.thread.ReentrantThreadExecutor;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class SchedulerImpl implements Scheduler {
private static final MinecraftClient mc = MinecraftClient.getInstance();
@Override
public void runSync(Runnable runnable) throws InterruptedException, ExecutionException, TimeoutException {
if (mc.isOnThread()) {
runnable.run();
} else {
executor.submit(() -> {
runnable.run();
return null;
}).get(30, TimeUnit.SECONDS);
}
}
@Override
public void runPostStartup(Runnable runnable) {
runLater(new Runnable() {
@Override
public void run() {
if (mc.overlay != null) {
// delay until after resources have been loaded
runLater(this);
return;
}
runnable.run();
}
});
}
/**
* Set when the currently running code has been scheduled by runLater.
* If this is the case, subsequent calls to runLater have to be delayed until all scheduled tasks have been
* processed, otherwise a livelock may occur.
*/
private boolean inRunLater = false;
private boolean inRenderTaskQueue = false;
// Starting 1.14 MC clears the queue of scheduled tasks on disconnect.
// This works fine for MC since it uses the queue only for packet handling but breaks our assumption that
// stuff submitted via runLater is actually always run (e.g. recording might not be fully stopped because parts
// of that are run via runLater and stopping the recording happens right around the time MC clears the queue).
// Luckily, that's also the version where MC pulled out the executor implementation, so we can just spin up our own.
public static class ReplayModExecutor extends ReentrantThreadExecutor<Runnable> {
private final Thread mcThread = Thread.currentThread();
private ReplayModExecutor(String string_1) {
super(string_1);
}
@Override
protected Runnable createTask(Runnable runnable) {
return runnable;
}
@Override
protected boolean canExecute(Runnable runnable) {
return true;
}
@Override
protected Thread getThread() {
return mcThread;
}
@Override
public void runTasks() {
super.runTasks();
}
}
public final ReplayModExecutor executor = new ReplayModExecutor("Client/ReplayMod");
@Override
public void runTasks() {
executor.runTasks();
}
@Override
public void runLaterWithoutLock(Runnable runnable) {
// MC 1.14+ no longer synchronizes on the queue while running its tasks
runLater(runnable);
}
@Override
public void runLater(Runnable runnable) {
runLater(runnable, () -> runLater(runnable));
}
private void runLater(Runnable runnable, Runnable defer) {
if (mc.isOnThread() && inRunLater && !inRenderTaskQueue) {
((MinecraftAccessor) mc).getRenderTaskQueue().offer(() -> {
inRenderTaskQueue = true;
try {
defer.run();
} finally {
inRenderTaskQueue = false;
}
});
} else {
executor.send(() -> {
inRunLater = true;
try {
runnable.run();
} catch (CrashException e) {
e.printStackTrace();
System.err.println(e.getReport().asString());
mc.setCrashReport(e.getReport());
} finally {
inRunLater = false;
}
});
}
}
}

View File

@@ -28,7 +28,7 @@ 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/" + ReplayMod.getMinecraftVersion();
private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + MCVer.getMinecraftVersion();
private ReplayMod mod;
@@ -86,7 +86,7 @@ public class OpenEyeExtra implements Extra {
GuiPopup popup = new GuiPopup(OfferGui.this);
new Thread(() -> {
try {
File targetFile = new File(mod.getMinecraft().runDirectory, "mods/" + ReplayMod.getMinecraftVersion() + "/OpenEye.jar");
File targetFile = new File(mod.getMinecraft().runDirectory, "mods/" + MCVer.getMinecraftVersion() + "/OpenEye.jar");
FileUtils.forceMkdir(targetFile.getParentFile());
HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection();

View File

@@ -3,6 +3,7 @@ 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.core.versions.MCVer;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.ServerInfoExt;
import com.replaymod.recording.Setting;
@@ -137,7 +138,7 @@ public class ConnectionEventHandler {
metaData.setCustomServerName(serverName);
metaData.setGenerator("ReplayMod v" + ReplayMod.instance.getVersion());
metaData.setDate(System.currentTimeMillis());
metaData.setMcVersion(ReplayMod.getMinecraftVersion());
metaData.setMcVersion(MCVer.getMinecraftVersion());
packetListener = new PacketListener(core, outputPath, replayFile, metaData);
Channel channel = ((NetworkManagerAccessor) networkManager).getChannel();
channel.pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener);

View File

@@ -1,5 +1,6 @@
package com.replaymod.replay;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.WrappedTimer;
import com.replaymod.core.versions.MCVer;
import com.replaymod.replay.camera.CameraController;
@@ -7,10 +8,6 @@ import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderTickCounter;
//#if MC>=11400
import com.replaymod.core.ReplayMod;
//#endif
//#if MC>=11400
import org.lwjgl.glfw.GLFW;
//#else
@@ -22,10 +19,7 @@ import org.lwjgl.glfw.GLFW;
//$$ import java.io.IOException;
//#else
//$$ import com.replaymod.replay.gui.screen.GuiOpeningReplay;
//$$ import cpw.mods.fml.common.eventhandler.Event;
//$$ import net.minecraft.client.renderer.entity.RenderManager;
//$$
//$$ import static com.replaymod.core.versions.MCVer.FML_BUS;
//#endif
//#endif
@@ -65,10 +59,9 @@ public class InputReplayTimer extends WrappedTimer {
//#endif
);
// 1.7.10: We have to run the scheduled executables (ours only) because MC would only run them every tick
ReplayMod.instance.runTasks();
//#if MC<=10710
//$$ FML_BUS.post(new RunScheduledTasks());
//$$
//$$ // Code below only updates the current screen when a world and player is loaded. This may not be the case for
//$$ // the GuiOpeningReplay screen resulting in a livelock.
//$$ // To counteract that, we always update that screen (doesn't matter if we do it twice).
@@ -77,10 +70,6 @@ public class InputReplayTimer extends WrappedTimer {
//$$ }
//#endif
//#if MC>=11400
ReplayMod.instance.executor.runTasks();
//#endif
// If we are in a replay, we have to manually process key and mouse events as the
// tick speed may vary or there may not be any ticks at all (when the replay is paused)
if (mod.getReplayHandler() != null && mc.world != null && mc.player != null) {
@@ -147,8 +136,4 @@ public class InputReplayTimer extends WrappedTimer {
}
}
}
//#if MC<=10710
//$$ public static class RunScheduledTasks extends Event {}
//#endif
}

View File

@@ -20,7 +20,7 @@
"environment": "client",
"entrypoints": {
"client": [
"com.replaymod.core.ReplayMod"
"com.replaymod.core.ReplayModBackend"
],
"modmenu": [
"com.replaymod.core.gui.ModMenuApiImpl"

View File

@@ -0,0 +1,85 @@
package com.replaymod.core;
import com.replaymod.core.mixin.MinecraftAccessor;
import net.minecraft.client.resources.IResourcePack;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import java.util.List;
import static com.replaymod.core.ReplayMod.MOD_ID;
import static com.replaymod.core.ReplayMod.jGuiResourcePack;
import static com.replaymod.core.versions.MCVer.getMinecraft;
//#if MC<=10710
//$$ import net.minecraft.client.resources.FolderResourcePack;
//$$ import java.io.ByteArrayInputStream;
//$$ import java.io.File;
//$$ import java.io.IOException;
//$$ import java.io.InputStream;
//$$ import java.nio.charset.StandardCharsets;
//#endif
@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",
//#endif
guiFactory = "com.replaymod.core.gui.GuiFactory")
public class ReplayModBackend {
private final ReplayMod mod = new ReplayMod(this);
@Deprecated
public static Configuration config;
@EventHandler
public void init(FMLPreInitializationEvent event) {
config = new Configuration(event.getSuggestedConfigurationFile());
config.load();
SettingsRegistry settingsRegistry = mod.getSettingsRegistry();
settingsRegistry.backend.setConfiguration(config);
settingsRegistry.save(); // Save default values to disk
}
@EventHandler
public void init(FMLInitializationEvent event) {
mod.initModules();
}
public String getVersion() {
return Loader.instance().getIndexedModList().get(MOD_ID).getVersion();
}
static { // Note: even preInit is too late and we'd have to issue another resource reload
List<IResourcePack> defaultResourcePacks = ((MinecraftAccessor) getMinecraft()).getDefaultResourcePacks();
if (jGuiResourcePack != null) {
defaultResourcePacks.add(jGuiResourcePack);
}
//#if MC<=10710
//$$ FolderResourcePack mainResourcePack = new FolderResourcePack(new File("../src/main/resources")) {
//$$ @Override
//$$ protected InputStream getInputStreamByName(String resourceName) throws IOException {
//$$ try {
//$$ return super.getInputStreamByName(resourceName);
//$$ } catch (IOException e) {
//$$ if ("pack.mcmeta".equals(resourceName)) {
//$$ return new ByteArrayInputStream(("{\"pack\": {\"description\": \"dummy pack for mod resources in dev-env\", \"pack_format\": 1}}").getBytes(StandardCharsets.UTF_8));
//$$ }
//$$ throw e;
//$$ }
//$$ }
//$$ };
//$$ defaultResourcePacks.add(mainResourcePack);
//#endif
}
}

View File

@@ -0,0 +1,131 @@
package com.replaymod.core.versions.scheduler;
import com.google.common.util.concurrent.ListenableFutureTask;
import com.replaymod.core.mixin.MinecraftAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ReportedException;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import java.util.Queue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
//#if MC<10800
//$$ import java.util.ArrayDeque;
//#endif
import static com.replaymod.core.versions.MCVer.FML_BUS;
public class SchedulerImpl implements Scheduler {
private static final Minecraft mc = Minecraft.getMinecraft();
@Override
public void runSync(Runnable runnable) throws InterruptedException, ExecutionException, TimeoutException {
if (mc.isCallingFromMinecraftThread()) {
runnable.run();
} else {
FutureTask<Void> future = new FutureTask<>(runnable, null);
runLater(future);
future.get(30, TimeUnit.SECONDS);
}
}
@Override
public void runPostStartup(Runnable runnable) {
runLater(runnable);
}
/**
* Set when the currently running code has been scheduled by runLater.
* If this is the case, subsequent calls to runLater have to be delayed until all scheduled tasks have been
* processed, otherwise a livelock may occur.
*/
private boolean inRunLater = false;
@Override
public void runLaterWithoutLock(Runnable runnable) {
runLater(() -> runLaterWithoutLock(runnable), runnable);
}
public void runLater(Runnable runnable) {
runLater(runnable, () -> runLater(runnable));
}
private void runLater(Runnable runnable, Runnable defer) {
if (mc.isCallingFromMinecraftThread() && inRunLater) {
//#if MC>=10800
FML_BUS.register(new Object() {
@SubscribeEvent
public void onRenderTick(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
FML_BUS.unregister(this);
defer.run();
}
}
});
//#else
//$$ FML_BUS.register(new RunLaterHelper(defer));
//#endif
return;
}
//#if MC>=10800
Queue<FutureTask<?>> tasks = ((MinecraftAccessor) mc).getScheduledTasks();
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (tasks) {
//#else
//$$ Queue<ListenableFutureTask<?>> tasks = scheduledTasks;
//$$ synchronized (scheduledTasks) {
//#endif
tasks.add(ListenableFutureTask.create(() -> {
inRunLater = true;
try {
runnable.run();
} catch (ReportedException e) {
e.printStackTrace();
System.err.println(e.getCrashReport().getCompleteReport());
mc.crashed(e.getCrashReport());
} finally {
inRunLater = false;
}
}, null));
}
}
//#if MC>=10800
@Override
public void runTasks() {
}
//#else
//$$ // 1.7.10: Cannot use MC's because it is processed only during ticks (so not at all when replay is paused)
//$$ private final Queue<ListenableFutureTask<?>> scheduledTasks = new ArrayDeque<>();
//$$
//$$ // in 1.7.10 apparently events can't be delivered to anonymous classes
//$$ public class RunLaterHelper {
//$$ private final Runnable defer;
//$$
//$$ private RunLaterHelper(Runnable defer) {
//$$ this.defer = defer;
//$$ }
//$$
//$$ @SubscribeEvent
//$$ public void onRenderTick(TickEvent.RenderTickEvent event) {
//$$ if (event.phase == TickEvent.Phase.START) {
//$$ FML_BUS.unregister(this);
//$$ defer.run();
//$$ }
//$$ }
//$$ }
//$$
//$$ @Override
//$$ public void runTasks() {
//$$ synchronized (scheduledTasks) {
//$$ while (!scheduledTasks.isEmpty()) {
//$$ scheduledTasks.poll().run();
//$$ }
//$$ }
//$$ }
//#endif
}

View File

@@ -0,0 +1,11 @@
package com.replaymod.core;
import net.minecraftforge.fml.ModList;
import static com.replaymod.core.ReplayMod.MOD_ID;
public class ReplayModBackend {
public String getVersion() {
return ModList.get().getModContainerById(MOD_ID).get().getModInfo().getVersion().toString();
}
}