From acd210f31718f4b51f01287194facc45c84e8630 Mon Sep 17 00:00:00 2001 From: FalsePattern <30945458+FalsePattern@users.noreply.github.com> Date: Fri, 10 Jun 2022 02:53:23 +0200 Subject: [PATCH] added update checks for my mods, turned fplib into a regular mod, added async lib --- gradle.properties | 2 +- .../lib/internal/CoreLoadingPlugin.java | 2 +- .../lib/internal/FalsePatternLib.java | 85 +++++++++---------- .../lib/internal/proxy/ClientProxy.java | 83 ++++++++++++++++++ .../lib/internal/proxy/CommonProxy.java | 67 +++++++++++++++ .../lib/updates/UpdateChecker.java | 8 +- .../assets/falsepatternlib/lang/en_US.lang | 10 ++- src/main/resources/pack.mcmeta | 1 + 8 files changed, 204 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/falsepattern/lib/internal/proxy/ClientProxy.java create mode 100644 src/main/java/com/falsepattern/lib/internal/proxy/CommonProxy.java create mode 100644 src/main/resources/pack.mcmeta diff --git a/gradle.properties b/gradle.properties index b675e7a..8b2dc9b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -76,7 +76,7 @@ mixinPreinitConfig = coreModClass = internal.CoreLoadingPlugin # If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class # that is annotated with @Mod) you want this to be true. When in doubt: leave it on false! -containsMixinsAndOrCoreModOnly = true +containsMixinsAndOrCoreModOnly = false # If enabled, you may use 'shadowImplementation' for dependencies. They will be integrated in your jar. It is your # responsibility check the licence and request permission for distribution, if required. diff --git a/src/main/java/com/falsepattern/lib/internal/CoreLoadingPlugin.java b/src/main/java/com/falsepattern/lib/internal/CoreLoadingPlugin.java index aa8b191..18c8957 100644 --- a/src/main/java/com/falsepattern/lib/internal/CoreLoadingPlugin.java +++ b/src/main/java/com/falsepattern/lib/internal/CoreLoadingPlugin.java @@ -20,7 +20,7 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin { @Override public String getModContainerClass() { - return Tags.GROUPNAME + ".internal.FalsePatternLib"; + return null; } @Override diff --git a/src/main/java/com/falsepattern/lib/internal/FalsePatternLib.java b/src/main/java/com/falsepattern/lib/internal/FalsePatternLib.java index 1b78588..45995b6 100644 --- a/src/main/java/com/falsepattern/lib/internal/FalsePatternLib.java +++ b/src/main/java/com/falsepattern/lib/internal/FalsePatternLib.java @@ -2,87 +2,84 @@ package com.falsepattern.lib.internal; import com.falsepattern.lib.config.ConfigException; import com.falsepattern.lib.config.ConfigurationManager; +import com.falsepattern.lib.internal.proxy.CommonProxy; +import com.falsepattern.lib.text.FormattedText; import com.falsepattern.lib.updates.ModUpdateInfo; import com.falsepattern.lib.updates.UpdateChecker; -import com.falsepattern.lib.util.ResourceUtil; -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; -import cpw.mods.fml.common.DummyModContainer; -import cpw.mods.fml.common.LoadController; -import cpw.mods.fml.common.Loader; -import cpw.mods.fml.common.MetadataCollection; +import cpw.mods.fml.common.*; import cpw.mods.fml.common.event.FMLConstructionEvent; -import cpw.mods.fml.common.event.FMLInterModComms; import cpw.mods.fml.common.event.FMLPostInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import lombok.Getter; -import lombok.SneakyThrows; import lombok.val; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.resources.I18n; +import net.minecraft.event.ClickEvent; import net.minecraft.launchwrapper.Launch; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; /** * Utility class used by FalsePatternLib's internal code. This can change between versions without notice, so do not use * this in your code! */ @SuppressWarnings("UnstableApiUsage") -public class FalsePatternLib extends DummyModContainer { +@Mod(modid = Tags.MODID, + name = Tags.MODNAME, + version = Tags.VERSION, + acceptedMinecraftVersions = "[1.7.10]", + acceptableRemoteVersions = "*") +public class FalsePatternLib { @Getter private static final Logger log = LogManager.getLogger(Tags.MODNAME); @Getter private static final boolean developerEnvironment = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); - private Future> updateCheckFuture; + @Getter + private static final ExecutorService asyncWorker = Executors.newSingleThreadExecutor((runnable) -> { + Thread thread = new Thread(runnable); + thread.setDaemon(true); + thread.setName(Tags.MODNAME + " Async Worker"); + return thread; + }); + + @SidedProxy(clientSide = Tags.GROUPNAME + ".internal.proxy.ClientProxy", serverSide = Tags.GROUPNAME + ".internal.proxy.CommonProxy") + private static CommonProxy proxy; public FalsePatternLib() { - super(MetadataCollection.from(ResourceUtil.getResourceFromJar("/mcmod.info", FalsePatternLib.class), Tags.MODID) - .getMetadataForId(Tags.MODID, null)); log.info("Version " + Tags.VERSION + " initialized!"); } - @SuppressWarnings("UnstableApiUsage") - @Subscribe + @Mod.EventHandler public void construct(FMLConstructionEvent e) { - ConfigurationManager.init(); - try { - ConfigurationManager.registerConfig(LibraryConfig.class); - } catch (ConfigException ex) { - getLog().error("Failed to register " + Tags.MODNAME + " config!", ex); - } + proxy.construct(e); } - @Subscribe + @Mod.EventHandler public void preInit(FMLPreInitializationEvent e) { - if (LibraryConfig.ENABLE_UPDATE_CHECKER) { - getLog().info("Launching asynchronous update check. I'll check back on it during postInit."); - updateCheckFuture = UpdateChecker.fetchUpdatesAsync("https://falsepattern.com/mc/versions.json"); - } + proxy.preInit(e); } - @Subscribe + @Mod.EventHandler public void postInit(FMLPostInitializationEvent e) { - if (updateCheckFuture != null && !updateCheckFuture.isCancelled()) { - try { - val updates = updateCheckFuture.get(); - if (updates != null) - for (val update: updates) - update.log(getLog()); - } catch (InterruptedException | ExecutionException ex) { - getLog().warn("Error while checking updates", ex); - } - - } + proxy.postInit(e); } - @SuppressWarnings("UnstableApiUsage") - @Override - public boolean registerBus(EventBus bus, LoadController controller) { - bus.register(this); - return true; - } } diff --git a/src/main/java/com/falsepattern/lib/internal/proxy/ClientProxy.java b/src/main/java/com/falsepattern/lib/internal/proxy/ClientProxy.java new file mode 100644 index 0000000..9abd853 --- /dev/null +++ b/src/main/java/com/falsepattern/lib/internal/proxy/ClientProxy.java @@ -0,0 +1,83 @@ +package com.falsepattern.lib.internal.proxy; + +import com.falsepattern.lib.internal.FalsePatternLib; +import com.falsepattern.lib.text.FormattedText; +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import lombok.val; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.resources.I18n; +import net.minecraft.event.ClickEvent; +import net.minecraft.util.IChatComponent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; + +@SideOnly(Side.CLIENT) +public class ClientProxy extends CommonProxy { + private Future> chatFuture; + + @Override + public void preInit(FMLPreInitializationEvent e) { + super.preInit(e); + MinecraftForge.EVENT_BUS.register(this); + } + + @Override + public void postInit(FMLPostInitializationEvent e) { + super.postInit(e); + chatFuture = FalsePatternLib.getAsyncWorker().submit(() -> { + long start = System.nanoTime(); + val updates = updatesFuture.get(); + if (updates == null || updates.size() == 0) return null; + val updateText = new ArrayList(FormattedText.parse(I18n.format("falsepatternlib.chat.updatesavailable")).toChatText()); + val mods = Loader.instance().getIndexedModList(); + for (val update : updates) { + val mod = mods.get(update.modID); + updateText.addAll(FormattedText.parse(I18n.format("falsepatternlib.chat.modname", mod.getName())).toChatText()); + updateText.addAll(FormattedText.parse(I18n.format("falsepatternlib.chat.currentversion", update.currentVersion)).toChatText()); + updateText.addAll(FormattedText.parse(I18n.format("falsepatternlib.chat.latestversion", update.latestVersion)).toChatText()); + if (!update.updateURL.isEmpty()) { + val pre = FormattedText.parse(I18n.format("falsepatternlib.chat.updateurlpre")).toChatText(); + val link = FormattedText.parse(I18n.format("falsepatternlib.chat.updateurl")).toChatText(); + val post = FormattedText.parse(I18n.format("falsepatternlib.chat.updateurlpost")).toChatText(); + pre.get(pre.size() - 1).appendSibling(link.get(0)); + link.get(link.size() - 1).appendSibling(post.get(0)); + for (val l: link) { + l.getChatStyle().setChatClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, update.updateURL)); + } + link.remove(0); + post.remove(0); + updateText.addAll(pre); + updateText.addAll(link); + updateText.addAll(post); + } + } + long end = System.nanoTime(); + FalsePatternLib.getLog().info("Constructed in {} ms", (end - start) / 1000000L); + return updateText; + }); + } + + @SubscribeEvent + public void onSinglePlayer(EntityJoinWorldEvent e) { + if (chatFuture == null || + !(e.entity instanceof EntityPlayerSP)) return; + val player = (EntityPlayerSP) e.entity; + try { + for (val line: chatFuture.get()) { + player.addChatMessage(line); + } + chatFuture = null; + } catch (Exception ex) { + FalsePatternLib.getLog().warn(ex); + } + } +} diff --git a/src/main/java/com/falsepattern/lib/internal/proxy/CommonProxy.java b/src/main/java/com/falsepattern/lib/internal/proxy/CommonProxy.java new file mode 100644 index 0000000..7960909 --- /dev/null +++ b/src/main/java/com/falsepattern/lib/internal/proxy/CommonProxy.java @@ -0,0 +1,67 @@ +package com.falsepattern.lib.internal.proxy; + +import com.falsepattern.lib.config.ConfigException; +import com.falsepattern.lib.config.ConfigurationManager; +import com.falsepattern.lib.internal.FalsePatternLib; +import com.falsepattern.lib.internal.LibraryConfig; +import com.falsepattern.lib.internal.Tags; +import com.falsepattern.lib.updates.ModUpdateInfo; +import com.falsepattern.lib.updates.UpdateChecker; +import cpw.mods.fml.common.event.FMLConstructionEvent; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import lombok.val; +import lombok.var; +import net.minecraftforge.common.MinecraftForge; + +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class CommonProxy { + + protected Future> updatesFuture; + + public void construct(FMLConstructionEvent e) { + ConfigurationManager.init(); + } + + public void preInit(FMLPreInitializationEvent e) { + try { + ConfigurationManager.registerConfig(LibraryConfig.class); + } catch (ConfigException ex) { + FalsePatternLib.getLog().error("Failed to register " + Tags.MODNAME + " config!", ex); + } + if (LibraryConfig.ENABLE_UPDATE_CHECKER) { + FalsePatternLib.getLog().info("Launching asynchronous update check."); + val updateCheckFuture = UpdateChecker.fetchUpdatesAsync("https://falsepattern.com/mc/versions.json"); + updatesFuture = FalsePatternLib.getAsyncWorker().submit(() -> { + if (!updateCheckFuture.isCancelled()) { + try { + var updates = updateCheckFuture.get(); + if (updates != null && updates.size() > 0) { + for (val update:updates) { + update.log(FalsePatternLib.getLog()); + } + } else if (updates == null) { + FalsePatternLib.getLog().warn("Unknown error while checking updates"); + } else { + FalsePatternLib.getLog().info("All FalsePattern mods up to date!"); + updates = null; + } + return updates; + } catch (InterruptedException | ExecutionException ex) { + FalsePatternLib.getLog().warn("Error while checking updates", ex); + } + } + return null; + }); + } + } + + public void postInit(FMLPostInitializationEvent e) { + + } + + +} diff --git a/src/main/java/com/falsepattern/lib/updates/UpdateChecker.java b/src/main/java/com/falsepattern/lib/updates/UpdateChecker.java index c9219f2..3f6fd86 100644 --- a/src/main/java/com/falsepattern/lib/updates/UpdateChecker.java +++ b/src/main/java/com/falsepattern/lib/updates/UpdateChecker.java @@ -27,12 +27,6 @@ import java.util.concurrent.atomic.AtomicReference; @StableAPI(since = "0.8.0") public class UpdateChecker { private static final AtomicInteger jsonLibraryLoaded = new AtomicInteger(0); - private static final ExecutorService asyncExecutor = Executors.newSingleThreadExecutor((runnable) -> { - Thread thread = new Thread(runnable); - thread.setDaemon(true); - thread.setName(Tags.MODNAME + " Asynchronous Update Check Thread"); - return thread; - }); /** * Same this as {@link #fetchUpdates(String)}, but defers the check to a different thread. Useful for asynchronous * update checks, if you don't want to block loading. @@ -40,7 +34,7 @@ public class UpdateChecker { * @return A future that will contain the update info about mods that were both available on the URL and installed */ public static Future> fetchUpdatesAsync(String url) { - return asyncExecutor.submit(() -> fetchUpdates(url)); + return FalsePatternLib.getAsyncWorker().submit(() -> fetchUpdates(url)); } /** diff --git a/src/main/resources/assets/falsepatternlib/lang/en_US.lang b/src/main/resources/assets/falsepatternlib/lang/en_US.lang index bbe9189..31079e4 100644 --- a/src/main/resources/assets/falsepatternlib/lang/en_US.lang +++ b/src/main/resources/assets/falsepatternlib/lang/en_US.lang @@ -1,4 +1,12 @@ config.falsepatternlib.updatecheck=Enable update checks config.falsepatternlib.updatecheck.tooltip=Used to control whether FalsePatternLib should check for outdated mods. config.falsepatternlib.disableinternet=FalsePatternLib Offline Mode -config.falsepatternlib.disableinternet.tooltip=Used to control whether FalsePatternLib should be allowed to use the internet. If this is enabled, update checks and library downloads will be completely blocked. \ No newline at end of file +config.falsepatternlib.disableinternet.tooltip=Used to control whether FalsePatternLib should be allowed to use the internet. If this is enabled, update checks and library downloads will be completely blocked. +falsepatternlib.chat.updatesavailable=§4Outdated mods detected! §r +falsepatternlib.chat.modname=§b§l[%s]§r +falsepatternlib.chat.currentversion= Current Version: §6%s§r +falsepatternlib.chat.latestversion= Latest Version: §2%s§r +falsepatternlib.chat.updateurlpre= Click +falsepatternlib.chat.updateurl= §n§o§dHERE§r +falsepatternlib.chat.updateurlpost= to visit update page +falsepatternlib.chat.updateavailablefor=§1§l[%s]§r Current version: §6%s§r Latest version: §2%s§r diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/main/resources/pack.mcmeta @@ -0,0 +1 @@ +{} \ No newline at end of file